diff --git a/.appveyor.yml b/.appveyor.yml index c1842b08d3a399693af837b8150541558c8e829f..f3b48d65c5492d80508179baee7f57aba61c14d5 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -15,7 +15,7 @@ environment: install: - git submodule update --init --recursive - call "%ProgramFiles(x86)%\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" x86 - - set PATH=C:\Qt\Tools\QtCreator\bin;C:\Qt\5.9.1\msvc2015\bin;%PATH% + - set PATH=C:\Qt\Tools\QtCreator\bin;C:\Qt\5.9.4\msvc2015\bin;%PATH% - mkdir %LOCALAPPDATA%\QtProject && copy test\qtlogging.ini %LOCALAPPDATA%\QtProject\ - ps: | Write-Host "Installing GStreamer..." -ForegroundColor Cyan @@ -35,7 +35,7 @@ install: Write-Host "Installed" -ForegroundColor Green build_script: - - mkdir %SHADOW_BUILD_DIR% && cd %SHADOW_BUILD_DIR% && C:\Qt\5.9.1\msvc2015\bin\qmake -r CONFIG-=debug_and_release CONFIG+=%CONFIG% CONFIG+=WarningsAsErrorsOn %APPVEYOR_BUILD_FOLDER%\qgroundcontrol.pro + - mkdir %SHADOW_BUILD_DIR% && cd %SHADOW_BUILD_DIR% && C:\Qt\5.9.4\msvc2015\bin\qmake -r CONFIG-=debug_and_release CONFIG+=%CONFIG% CONFIG+=WarningsAsErrorsOn %APPVEYOR_BUILD_FOLDER%\qgroundcontrol.pro - cd %SHADOW_BUILD_DIR% && jom - if "%CONFIG%" EQU "installer" ( copy %SHADOW_BUILD_DIR%\release\QGroundControl-installer.exe %APPVEYOR_BUILD_FOLDER%\QGroundControl-installer.exe ) # Generate the source server information to embed in the PDB diff --git a/.travis.yml b/.travis.yml index d49f311900fc456d7a55f7cb3699f87862fa0372..c634ee4a3d724a1b0cce8d926e19ff5003701574 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,8 +4,6 @@ language: cpp env: global: - - CCACHE_CPP2=1 - - CCACHE_DISABLE=1 - JOBS=4 - QT_FATAL_WARNINGS=1 - SHADOW_BUILD_DIR=/tmp/shadow_build_dir @@ -28,18 +26,18 @@ matrix: env: SPEC=android-g++ CONFIG=installer sudo: false - os: osx - osx_image: xcode8.3 + osx_image: xcode9.2 env: SPEC=macx-clang CONFIG=installer sudo: required + - os: osx + osx_image: xcode9.2 + env: SPEC=macx-ios-clang CONFIG=release + sudo: false # OSX builds pared back to installer only since travis sucks so bad we can't afford more than one' # - os: osx # osx_image: xcode8 # env: SPEC=macx-clang CONFIG=debug # sudo: required -# - os: osx -# osx_image: xcode8 -# env: SPEC=macx-ios-clang CONFIG=release -# sudo: false android: components: @@ -50,7 +48,6 @@ android: addons: apt: packages: - - ccache - espeak - libespeak-dev - libgstreamer-plugins-base1.0-dev @@ -60,25 +57,12 @@ addons: - libudev-dev - wget -cache: - directories: - - $HOME/.ccache - before_install: # fetch entire git repo to properly determine the version - if [ "${CONFIG}" = "installer" ]; then cd ${TRAVIS_BUILD_DIR} && git fetch --unshallow && git fetch --all --tags; fi - # install ccache for osx/ios - - if [ "${TRAVIS_OS_NAME}" = "osx" ]; then - wget --quiet https://s3.amazonaws.com/px4-travis/toolchain/macos/ccache && - chmod +x ccache && sudo mv ccache /usr/local/bin; - fi - - # setup ccache - #- ccache -M 500MB && ccache -z - # compile threads - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then export JOBS=$((`cat /proc/cpuinfo | grep -c ^processor`+1)); @@ -89,9 +73,9 @@ before_install: install: # linux dependencies: qt - if [ "${SPEC}" = "linux-g++-64" ]; then - wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.1-linux-min.tar.bz2 && - tar jxf Qt5.9.1-linux-min.tar.bz2 -C /tmp && - export PATH=/tmp/Qt5.9-linux/5.9.1/gcc_64/bin:$PATH && + wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.3-gcc_64-min.tar.bz2 && + tar jxf Qt5.9.3-gcc_64-min.tar.bz2 -C /tmp && + export PATH=/tmp/Qt5.9-gcc_64/5.9.3/gcc_64/bin:$PATH && export DISPLAY=:99.0 && sh -e /etc/init.d/xvfb start ; @@ -99,8 +83,8 @@ install: # android dependencies: qt, gstreamer, android-ndk - if [ "${SPEC}" = "android-g++" ]; then - wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.1-android-min.tar.bz2 && - tar jxf Qt5.9.1-android-min.tar.bz2 -C /tmp && + wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.3-android_armv7-min.tar.bz2 && + tar jxf Qt5.9.3-android_armv7-min.tar.bz2 -C /tmp && wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-android-armv7-1.5.2.tar.bz2 && mkdir -p ${TRAVIS_BUILD_DIR}/gstreamer-1.0-android-armv7-1.5.2 && tar jxf gstreamer-1.0-android-armv7-1.5.2.tar.bz2 -C ${TRAVIS_BUILD_DIR}/gstreamer-1.0-android-armv7-1.5.2 && @@ -109,35 +93,53 @@ install: ./android-ndk-r10e-linux-x86_64.bin > /dev/null && export ANDROID_NDK_ROOT=`pwd`/android-ndk-r10e && export ANDROID_SDK_ROOT=/usr/local/android-sdk && - export PATH=/tmp/Qt5.9-android/5.9.1/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH && echo $PATH + export PATH=/tmp/Qt5.9-android_armv7/5.9.3/android_armv7/bin:`pwd`/android-ndk-r10e:$PATH && echo $PATH ; fi - # osx dependencies: qt, gstreamer, gstreamer-devel + # osx dependencies: qt (master builds only: gstreamer, gstreamer-devel) - if [ "${SPEC}" = "macx-clang" ]; then - wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.1-mac-clang-min.tar.bz2 && - tar jxf Qt5.9.1-mac-clang-min.tar.bz2 -C /tmp && + wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.3-clang_64-min.tar.bz2 && + tar jxf Qt5.9.3-clang_64-min.tar.bz2 -C /tmp + ; + fi + + - if [[ "${SPEC}" = "macx-clang" && "${TRAVIS_PULL_REQUEST}" = "false" ]]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-1.5.2-x86_64.pkg && - sudo installer -verboseR -pkg gstreamer-1.0-1.5.2-x86_64.pkg -target / && + sudo installer -verboseR -pkg gstreamer-1.0-1.5.2-x86_64.pkg -target / + ; + fi + + - if [[ "${SPEC}" = "macx-clang" && "${TRAVIS_PULL_REQUEST}" = "false" ]]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/gstreamer-1.0-devel-1.5.2-x86_64.pkg && - sudo installer -verboseR -pkg gstreamer-1.0-devel-1.5.2-x86_64.pkg -target / && + sudo installer -verboseR -pkg gstreamer-1.0-devel-1.5.2-x86_64.pkg -target / + ; + fi + + - if [[ "${SPEC}" = "macx-clang" && "${TRAVIS_PULL_REQUEST}" = "false" ]]; then wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/osx-gstreamer.tar.bz2 && - sudo tar jxf osx-gstreamer.tar.bz2 -C /Library/Frameworks && - export QT_DIR=Qt5.9-mac-clang/5.9.1/clang_64 && + sudo tar jxf osx-gstreamer.tar.bz2 -C /Library/Frameworks + ; + fi + + - if [ "${SPEC}" = "macx-clang" ]; then + export QT_DIR=Qt5.9-clang_64/5.9.3/clang_64 && export QT_QPA_PLATFORM_PLUGIN_PATH=/tmp/$QT_DIR/plugins && export QML2_IMPORT_PATH=/tmp/$QT_DIR/qml && export PATH=/tmp/$QT_DIR/bin:$PATH ; fi - # ios dependencies: qt, TODO: add gstreamer + # ios dependencies: qt + - if [ "${SPEC}" = "macx-ios-clang" ]; then + wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.9.3-ios-min.tar.bz2 + ; + fi - if [ "${SPEC}" = "macx-ios-clang" ]; then - wget --quiet https://s3-us-west-2.amazonaws.com/qgroundcontrol/dependencies/Qt5.8.0-ios-min.tar.bz2 && - tar jxf Qt5.8.0-ios-min.tar.bz2 -C /tmp && + tar jxf Qt5.9.3-ios-min.tar.bz2 -C /tmp && export IOS_CCACHE_CC=`/usr/bin/xcrun -sdk iphoneos -find clang` && export IOS_CCACHE_CXX=`/usr/bin/xcrun -sdk iphoneos -find clang++` && - export PATH=/tmp/Qt5.8-ios/5.8/ios/bin:$PATH && - tools/patch_qt_for_xcode8.sh + export PATH=/tmp/Qt5.9-ios/5.9.3/ios/bin:$PATH ; fi diff --git a/Jenkinsfile b/Jenkinsfile index 56e7fed77127117fe94d8ecf7a93275149c2a305..649233dbb9f40894aed1fc4c1bb6e1dfc7d927e0 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -2,9 +2,103 @@ pipeline { agent any stages { stage('build') { - steps { - sh 'git status' + parallel { + stage('Linux Debug') { + environment { + CCACHE_BASEDIR = "${env.WORKSPACE}" + QGC_CONFIG = 'debug' + QMAKE_VER = "5.9.2/gcc_64/bin/qmake" + } + agent { + docker { + image 'mavlink/qgc-build-linux' + args '-v ${CCACHE_DIR}:${CCACHE_DIR}:rw' + } + } + steps { + sh 'export' + sh 'ccache -z' + sh 'git submodule deinit -f .' + sh 'git clean -ff -x -d .' + sh 'git submodule update --init --recursive --force' + sh 'mkdir build; cd build; ${QT_PATH}/${QMAKE_VER} -r ${WORKSPACE}/qgroundcontrol.pro CONFIG+=${QGC_CONFIG} CONFIG+=WarningsAsErrorsOn' + sh 'cd build; make -j`nproc --all`' + sh 'ccache -s' + } + } + stage('Linux Release') { + environment { + CCACHE_BASEDIR = "${env.WORKSPACE}" + QGC_CONFIG = 'release' + QMAKE_VER = "5.9.2/gcc_64/bin/qmake" + } + agent { + docker { + image 'mavlink/qgc-build-linux' + args '-v ${CCACHE_DIR}:${CCACHE_DIR}:rw' + } + } + steps { + sh 'export' + sh 'ccache -z' + sh 'git submodule deinit -f .' + sh 'git clean -ff -x -d .' + sh 'git submodule update --init --recursive --force' + sh 'mkdir build; cd build; ${QT_PATH}/${QMAKE_VER} -r ${WORKSPACE}/qgroundcontrol.pro CONFIG+=${QGC_CONFIG} CONFIG+=WarningsAsErrorsOn' + sh 'cd build; make -j`nproc --all`' + sh 'ccache -s' + } + } + stage('OSX Debug') { + agent { + node { + label 'mac' + } + } + environment { + CCACHE_BASEDIR = "${env.WORKSPACE}" + QGC_CONFIG = 'debug' + QMAKE_VER = "5.9.3/clang_64/bin/qmake" + } + steps { + sh 'export' + sh 'ccache -z' + sh 'git submodule deinit -f .' + sh 'git clean -ff -x -d .' + sh 'git submodule update --init --recursive --force' + sh 'mkdir build; cd build; ${QT_PATH}/${QMAKE_VER} -r ${WORKSPACE}/qgroundcontrol.pro CONFIG+=${QGC_CONFIG} CONFIG+=WarningsAsErrorsOn' + sh 'cd build; make -j`sysctl -n hw.ncpu`' + sh 'ccache -s' + } + } + stage('OSX Release') { + agent { + node { + label 'mac' + } + } + environment { + CCACHE_BASEDIR = "${env.WORKSPACE}" + QGC_CONFIG = 'release' + QMAKE_VER = "5.9.3/clang_64/bin/qmake" + } + steps { + sh 'export' + sh 'ccache -z' + sh 'git submodule deinit -f .' + sh 'git clean -ff -x -d .' + sh 'git submodule update --init --recursive --force' + sh 'mkdir build; cd build; ${QT_PATH}/${QMAKE_VER} -r ${WORKSPACE}/qgroundcontrol.pro CONFIG+=${QGC_CONFIG} CONFIG+=WarningsAsErrorsOn' + sh 'cd build; make -j`sysctl -n hw.ncpu`' + sh 'ccache -s' + } + } } } } -} \ No newline at end of file + environment { + CCACHE_CPP2 = '1' + CCACHE_DIR = '/tmp/ccache' + QT_FATAL_WARNINGS = '1' + } +} diff --git a/QGCCommon.pri b/QGCCommon.pri index 0a774aa556939130ed7e1d312d847f4fb52d4d7c..f26540dd1752634c2cbe3f008bb88af726ac556c 100644 --- a/QGCCommon.pri +++ b/QGCCommon.pri @@ -83,7 +83,7 @@ linux { DEFINES += NO_SERIAL_LINK DEFINES += QGC_DISABLE_UVC QMAKE_IOS_DEPLOYMENT_TARGET = 8.0 - QMAKE_IOS_TARGETED_DEVICE_FAMILY = 1,2 # Universal + QMAKE_APPLE_TARGETED_DEVICE_FAMILY = 1,2 # Universal QMAKE_LFLAGS += -Wl,-no_pie } else { error("Unsupported build platform, only Linux, Windows, Android and Mac (Mac OS and iOS) are supported") @@ -235,7 +235,9 @@ ReleaseBuild { DEFINES += QT_NO_DEBUG QT_MESSAGELOGCONTEXT CONFIG += force_debug_info # Enable debugging symbols on release builds !iOSBuild { - CONFIG += ltcg # Turn on link time code generation + !AndroidBuild { + CONFIG += ltcg # Turn on link time code generation + } } WindowsBuild { diff --git a/QGCExternalLibs.pri b/QGCExternalLibs.pri index 06cb2e1fb9dbc95f093276ffb4d7d49351acdaf9..fa73a0bb438eae4bc5ab4ec9644c86182f689ff6 100644 --- a/QGCExternalLibs.pri +++ b/QGCExternalLibs.pri @@ -7,20 +7,53 @@ WindowsBuild { # # [REQUIRED] Add support for the MAVLink communications protocol. -# Mavlink dialect is hardwired to arudpilotmega for now. The reason being +# +# By default MAVLink dialect is hardwired to arudpilotmega. The reason being # the current codebase supports both PX4 and APM flight stack. PX4 flight stack -# only usese common mavlink specifications, whereas APM flight stack uses custom -# mavlink specifications which add to common. So by using the adupilotmega dialect +# only uses common MAVLink specifications, whereas APM flight stack uses custom +# MAVLink specifications which adds to common. So by using the adupilotmega dialect # QGC can support both in the same codebase. -# + # Once the mavlink helper routines include support for multiple dialects within # a single compiled codebase this hardwiring of dialect can go away. But until then # this "workaround" is needed. -MAVLINKPATH_REL = libs/mavlink/include/mavlink/v2.0 -MAVLINKPATH = $$BASEDIR/$$MAVLINKPATH_REL -MAVLINK_CONF = ardupilotmega -DEFINES += MAVLINK_NO_DATA +# In the mean time, it’s possible to define a completely different dialect by defining the +# location and name below. + +# check for user defined settings in user_config.pri if not already set as qmake argument +isEmpty(MAVLINKPATH_REL) { + exists(user_config.pri):infile(user_config.pri, MAVLINKPATH_REL) { + MAVLINKPATH_REL = $$fromfile(user_config.pri, MAVLINKPATH_REL) + message($$sprintf("Using user-supplied relativ mavlink path '%1' specified in user_config.pri", $$MAVLINKPATH_REL)) + } else { + MAVLINKPATH_REL = libs/mavlink/include/mavlink/v2.0 + } +} + +isEmpty(MAVLINKPATH) { + exists(user_config.pri):infile(user_config.pri, MAVLINKPATH) { + MAVLINKPATH = $$fromfile(user_config.pri, MAVLINKPATH) + message($$sprintf("Using user-supplied mavlink path '%1' specified in user_config.pri", $$MAVLINKPATH)) + } else { + MAVLINKPATH = $$BASEDIR/$$MAVLINKPATH_REL + } +} + +isEmpty(MAVLINK_CONF) { + exists(user_config.pri):infile(user_config.pri, MAVLINK_CONF) { + MAVLINK_CONF = $$fromfile(user_config.pri, MAVLINK_CONF) + message($$sprintf("Using user-supplied mavlink dialect '%1' specified in user_config.pri", $$MAVLINK_CONF)) + } else { + MAVLINK_CONF = ardupilotmega + } +} + +# If defined, all APM specific MAVLink messages are disabled +contains (CONFIG, QGC_DISABLE_APM_MAVLINK) { + message("Disable APM MAVLink support") + DEFINES += NO_ARDUPILOT_DIALECT +} # First we select the dialect, checking for valid user selection # Users can override all other settings by specifying MAVLINK_CONF as an argument to qmake diff --git a/QGCInstaller.pri b/QGCInstaller.pri index 9873915e882d01f3cbb6aa656aa41c795a8755c6..443ca699e8203120edc641b2a367886f73e2772e 100644 --- a/QGCInstaller.pri +++ b/QGCInstaller.pri @@ -26,13 +26,14 @@ installer { # We cd to release directory so we can run macdeployqt without a path to the # qgroundcontrol.app file. If you specify a path to the .app file the symbolic # links to plugins will not be created correctly. - QMAKE_POST_LINK += && mkdir -p $${DESTDIR}/package QMAKE_POST_LINK += && cd $${DESTDIR} && $$dirname(QMAKE_QMAKE)/macdeployqt $${TARGET}.app -appstore-compliant -verbose=2 -qmldir=$${BASEDIR}/src # macdeploy is missing some relocations once in a while. "Fix" it: QMAKE_POST_LINK += && python $$BASEDIR/tools/osxrelocator.py $${TARGET}.app/Contents @rpath @executable_path/../Frameworks -r > /dev/null 2>&1 # Create package - QMAKE_POST_LINK += && cd $${OUT_PWD} - QMAKE_POST_LINK += && hdiutil create -verbose -stretch 4g -layout SPUD -srcfolder $${DESTDIR}/$${TARGET}.app -volname $${TARGET} $${DESTDIR}/package/$${TARGET}.dmg + QMAKE_POST_LINK += && hdiutil create /tmp/tmp.dmg -ov -volname "$${TARGET}-$${MAC_VERSION}" -fs HFS+ -srcfolder "$${DESTDIR}/" + QMAKE_POST_LINK += && mkdir -p $${DESTDIR}/package + QMAKE_POST_LINK += && hdiutil convert /tmp/tmp.dmg -format UDBZ -o $${DESTDIR}/package/$${TARGET}.dmg + QMAKE_POST_LINK += && rm /tmp/tmp.dmg } WindowsBuild { QMAKE_POST_LINK += $$escape_expand(\\n) cd $$BASEDIR_WIN && $$quote("\"C:\\Program Files \(x86\)\\NSIS\\makensis.exe\"" /DINSTALLER_ICON="\"$${QGC_INSTALLER_ICON}\"" /DHEADER_BITMAP="\"$${QGC_INSTALLER_HEADER_BITMAP}\"" /DAPPNAME="\"$${QGC_APP_NAME}\"" /DEXENAME="\"$${TARGET}\"" /DORGNAME="\"$${QGC_ORG_NAME}\"" /DDESTDIR=$${DESTDIR} /NOCD "\"/XOutFile $${DESTDIR_WIN}\\$${TARGET}-installer.exe\"" "$$BASEDIR_WIN\\deploy\\qgroundcontrol_installer.nsi") diff --git a/QGCSetup.pri b/QGCSetup.pri index 48e400b517503d1260e75a28480dfe93cffb10b5..32a4803b903793cdd68ecb9f54f766180f0f37c7 100644 --- a/QGCSetup.pri +++ b/QGCSetup.pri @@ -40,7 +40,7 @@ WindowsBuild { # Perform platform specific setup # -iOSBuild | MacBuild { +MacBuild { # Update version info in bundle QMAKE_POST_LINK += && /usr/libexec/PlistBuddy -c \"Set :CFBundleShortVersionString $${MAC_VERSION}\" $$DESTDIR/$${TARGET}.app/Contents/Info.plist QMAKE_POST_LINK += && /usr/libexec/PlistBuddy -c \"Set :CFBundleVersion $${MAC_BUILD}\" $$DESTDIR/$${TARGET}.app/Contents/Info.plist @@ -146,7 +146,8 @@ LinuxBuild { platforminputcontexts \ platforms \ position \ - sqldrivers + sqldrivers \ + texttospeech !contains(DEFINES, __rasp_pi2__) { QT_PLUGIN_LIST += xcbglintegrations diff --git a/README.md b/README.md index 7243cf71e8de9903e5a559a50f60da3164b644fd..b74050dd6632a86a24abee5c2109c46c9cbc94cc 100644 --- a/README.md +++ b/README.md @@ -1,26 +1,15 @@ # QGroundControl Ground Control Station -## Open Source Micro Air Vehicle Ground Control Station - [![Releases](https://img.shields.io/github/release/mavlink/QGroundControl.svg)](https://github.com/mavlink/QGroundControl/releases) [![Travis Build Status](https://travis-ci.org/mavlink/qgroundcontrol.svg?branch=master)](https://travis-ci.org/mavlink/qgroundcontrol) [![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/crxcm4qayejuvh6c/branch/master?svg=true)](https://ci.appveyor.com/project/mavlink/qgroundcontrol) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mavlink/qgroundcontrol?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -The license terms are set in the COPYING.md file. - -* Project: - - -* Files: - - -* Credits: - - +Website: ## Obtaining source code + Source code for QGroundControl is kept on GitHub: https://github.com/mavlink/qgroundcontrol. ``` git clone --recursive https://github.com/mavlink/qgroundcontrol.git @@ -36,24 +25,25 @@ https://docs.qgroundcontrol.com/en/ #### Native Builds QGroundControl builds are supported for OSX, Linux, Windows, iOS and Android. QGroundControl uses [Qt](http://www.qt.io) as its cross-platform support library and uses [QtCreator](http://doc.qt.io/qtcreator/index.html) as its default build environment. + * OSX: OSX 10.7 or higher, 64 bit, clang compiler (IMPORTANT: XCode 8 requires a workaround described below) * Ubuntu: 64 bit, gcc compiler * Windows: Vista or higher, 32 bit, [Visual Studio 2015 compiler](http://www.visualstudio.com/downloads/download-visual-studio-vs#d-express-windows-desktop) * iOS: 8.0 and higher * Android: Jelly Bean (4.1) and higher. Standard QGC is built against ndk version 19. -* Qt version: 5.9.1 ONLY +* Qt version: **5.9.3 only** ###### Install QT -You need to install Qt as described below instead of using pre-built packages from say, a Linux distribution, because QGroundControl needs access to private Qt headers. +You **need to install Qt as described below** instead of using pre-built packages from say, a Linux distribution, because QGroundControl needs access to private Qt headers. * Download the [Qt installer](http://www.qt.io/download-open-source) - * Make sure to install Qt version **5.9.1**. You will also need to install the Qt Speech package. + * Make sure to install Qt version **5.9.3**. You will also need to install the Qt Speech package. * Ubuntu: Set the downloaded file to executable using:`chmod +x`. Install to default location for use with ./qgroundcontrol-start.sh. If you install Qt to a non-default location you will need to modify qgroundcontrol-start.sh in order to run downloaded builds. * Windows: Make sure to install VS 2015 32 bit package. ###### Install additional packages: -* Ubuntu: sudo apt-get install espeak libespeak-dev libudev-dev libsdl2-dev -* Fedora: sudo dnf install espeak espeak-devel SDL2-devel SDL2 systemd-devel -* Arch Linux: pacman -Sy espeak +* Ubuntu: sudo apt-get install speech-dispatcher libudev-dev libsdl2-dev libgstreamer1.0-0 gstreamer1.0-plugins-base libgstreamer-plugins-base1.0-dev gstreamer1.0* +* Fedora: sudo dnf install speech-dispatcher SDL2-devel SDL2 systemd-devel +* Arch Linux: pacman -Sy speech-dispatcher * Windows: [USB Driver](http://www.pixhawk.org/firmware/downloads) to connect to Pixhawk/PX4Flow/3DR Radio * Android: [Qt Android Setup](http://doc.qt.io/qt-5/androidgs.html) @@ -61,10 +51,10 @@ You need to install Qt as described below instead of using pre-built packages fr * Launch Qt Creator and open the `qgroundcontrol.pro` project. * Select the appropriate kit for your needs: - * OSX: Desktop Qt 5.9.1 clang 64 bit - * Ubuntu: Desktop Qt 5.9.1 GCC bit - * Windows: Desktop Qt 5.9.1 MSVC2015 32bit - * Android: Android for armeabi-v7a (GCC 4.9, Qt 5.9.1) + * OSX: Desktop Qt 5.9.3 clang 64 bit + * Ubuntu: Desktop Qt 5.9.3 GCC bit + * Windows: Desktop Qt 5.9.3 MSVC2015 32bit + * Android: Android for armeabi-v7a (GCC 4.9, Qt 5.9.3) * Note: iOS builds must be built using xCode: http://doc.qt.io/qt-5/ios-support.html. Use Qt Creator to generate the XCode project (*Run Qmake* from the context menu). #### Vagrant diff --git a/UnitTest.qrc b/UnitTest.qrc index 61510fde340a034be48ab0aee7593c72c27ebf2d..2bfa5bbb8062a9838d5fd3351bc49e074e13939d 100644 --- a/UnitTest.qrc +++ b/UnitTest.qrc @@ -9,9 +9,10 @@ src/MissionManager/UnitTest/MavCmdInfoVTOL.json src/MissionManager/UnitTest/MissionPlanner.waypoints src/MissionManager/UnitTest/OldFileFormat.mission - src/MissionManager/UnitTest/GoodPolygon.kml - src/MissionManager/UnitTest/MissingPolygonNode.kml - src/MissionManager/UnitTest/BadXml.kml - src/MissionManager/UnitTest/BadCoordinatesNode.kml + src/MissionManager/UnitTest/PolygonAreaTest.kml + src/MissionManager/UnitTest/PolygonGood.kml + src/MissionManager/UnitTest/PolygonMissingNode.kml + src/MissionManager/UnitTest/PolygonBadXml.kml + src/MissionManager/UnitTest/PolygonBadCoordinatesNode.kml diff --git a/Vagrantfile b/Vagrantfile index e15e22d1f6351010a54d350356b5e8ec301323bd..a3df179e62ca9ac7c92c5ce68233df413e7794b9 100644 --- a/Vagrantfile +++ b/Vagrantfile @@ -15,7 +15,7 @@ Vagrant.configure(2) do |config| config.vm.provider "virtualbox" config.vm.provider "vmware_fusion" - config.vm.box = "boxcutter/ubuntu1604" + config.vm.box = "ubuntu/xenial64" config.vm.provider :docker do |docker, override| override.vm.box = "tknerr/baseimage-ubuntu-16.04" end diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 7c0efd00c1b7cc0b9ff0b874995b89c57e736219..d78675443011f3abe13e9cd6e17302842f04620b 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -1,6 +1,6 @@ - + @@ -59,6 +59,7 @@ + diff --git a/deploy/MakeQtTravisTarball.sh b/deploy/MakeQtTravisTarball.sh new file mode 100755 index 0000000000000000000000000000000000000000..7d9d9e27cd6cbeff6b4803e7e627573d8af6290a --- /dev/null +++ b/deploy/MakeQtTravisTarball.sh @@ -0,0 +1,31 @@ +#!/bin/bash -x + +if [[ $# -eq 0 ]]; then + echo 'MakeQtTravisTarball.sh QtDirectory QtFullVersion QtBaseVersion BuildType' + exit 1 +fi + +QT_DIRECTORY=$1 +if [ ! -d ${QT_DIRECTORY} ]; then + echo 'Specify directory for Qt Directory.' + exit 1 +fi + +QT_FULL_VERSION=$2 +if [ ! -d ${QT_DIRECTORY}/${QT_FULL_VERSION} ]; then + echo 'Qt version directory not found' + exit 1 +fi + +QT_BASE_VERSION=$3 + +QT_BUILD_TYPE=$4 +if [ ! -d ${QT_DIRECTORY}/${QT_FULL_VERSION}/${QT_BUILD_TYPE} ]; then + echo 'Qt build type directory not found' + exit 1 +fi + +mkdir -p Qt${QT_BASE_VERSION}-${QT_BUILD_TYPE}/${QT_FULL_VERSION}/${QT_BUILD_TYPE} +cp -r ${QT_DIRECTORY}/${QT_FULL_VERSION}/${QT_BUILD_TYPE} Qt${QT_BASE_VERSION}-${QT_BUILD_TYPE}/${QT_FULL_VERSION} +rm -rf Qt${QT_BASE_VERSION}-${QT_BUILD_TYPE}/${QT_FULL_VERSION}/${QT_BUILD_TYPE}/doc +tar -jcvf Qt${QT_FULL_VERSION}-${QT_BUILD_TYPE}-min.tar.bz2 Qt${QT_BASE_VERSION}-${QT_BUILD_TYPE}/ diff --git a/deploy/create_linux_appimage.sh b/deploy/create_linux_appimage.sh index 57ecb2dd7b3e4b211edc56c6dd31e20d43f24f15..b3809619f558b87f039909b45705b3f101dc14b3 100755 --- a/deploy/create_linux_appimage.sh +++ b/deploy/create_linux_appimage.sh @@ -34,10 +34,9 @@ mkdir -p ${APPDIR} cd ${TMPDIR} wget -c --quiet http://ftp.us.debian.org/debian/pool/main/u/udev/udev_175-7.2_amd64.deb -wget -c --quiet http://ftp.us.debian.org/debian/pool/main/e/espeak/espeak_1.46.02-2_amd64.deb +wget -c --quiet http://ftp.us.debian.org/debian/pool/main/s/speech-dispatcher/speech-dispatcher_0.8.8-1_amd64.deb wget -c --quiet http://ftp.us.debian.org/debian/pool/main/libs/libsdl2/libsdl2-2.0-0_2.0.2%2bdfsg1-6_amd64.deb - cd ${APPDIR} find ../ -name *.deb -exec dpkg -x {} . \; diff --git a/libs/mavlink/include/mavlink/v2.0 b/libs/mavlink/include/mavlink/v2.0 index 5be9f5bf6002d58199cafafb5d416929fb8d8bcd..3ddcbdfcd360c846246d8905d877add3488c8363 160000 --- a/libs/mavlink/include/mavlink/v2.0 +++ b/libs/mavlink/include/mavlink/v2.0 @@ -1 +1 @@ -Subproject commit 5be9f5bf6002d58199cafafb5d416929fb8d8bcd +Subproject commit 3ddcbdfcd360c846246d8905d877add3488c8363 diff --git a/localization/gen_translation_source.sh b/localization/gen_translation_source.sh index 02061caa13e64cf20f1bd6181e402198aa6ebd63..83d30696a7938faf4629d92eaa7a9bdcf53fe8ef 100755 --- a/localization/gen_translation_source.sh +++ b/localization/gen_translation_source.sh @@ -1,5 +1,5 @@ #!/bin/bash # This is set to find lupdate in my particular installation. You will need to set the path # where you have Qt installed. -QT_PATH=~/Applications/Qt/5.7/clang_64/bin +QT_PATH=~/Applications/Qt/5.9.1/clang_64/bin $QT_PATH/lupdate ../src -ts qgc.ts diff --git a/localization/qgc.ts b/localization/qgc.ts index 25c9ba8c5fbb3fcee148585719d86be5bc899db1..27dea14b3baf926123e043453e863d30fcbad06c 100644 --- a/localization/qgc.ts +++ b/localization/qgc.ts @@ -4,32 +4,26 @@ APMAirframeComponent - - - Select your drone to load the default parameters for it. - - - - + + Please select your airframe type - - - - - Load common parameters + + + Frame Class: - - Frame Class: + + + Frame Type: - - Frame Type: + + Airframe @@ -54,22 +48,27 @@ APMAirframeComponentSummary + + Frame Type: + Frame Class: + Firmware Version: + Unknown @@ -78,138 +77,167 @@ APMCameraComponent + Disabled + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Gimbal + Stabilize + Servo reverse + Output channel: + Input channel: + Gimbal angle limits: + + min + + max + Servo PWM limits: + Gimbal Settings + Type: + Gimbal Type changes takes affect next reboot of autopilot + Default Mode: + Tilt + Roll + Pan @@ -228,21 +256,25 @@ APMCameraComponentSummary + Gimbal type: + Tilt input channel: + Pan input channel: + Roll input channel: @@ -251,84 +283,139 @@ APMFirmwarePlugin - + + QGroundControl fully supports Version %1.%2 and above. You are using a version prior to that. This combination is untested, you may run into unpredictable results. + + + + Error during Solo video link setup: %1 + + + Unable to change altitude, vehicle altitude not known. + + + + + Vehicle does not support guided takeoff + + + + + Unable to takeoff, vehicle position not known. + + + + + Unable to takeoff: Vehicle failed to change to Guided mode. + + + + + Unable to takeoff: Vehicle failed to arm. + + + + + Unable to start mission. Vehicle takeoff failed. + + + + + Unable to start mission. Vehicle failed to change to auto. + + APMFlightModesComponent + Flight Mode Settings + (Channel 5) + Flight mode channel: + Not assigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Flight Mode + Channel Options + Channel option %1 : @@ -347,31 +434,37 @@ APMFlightModesComponentSummary + Flight Mode 1: + Flight Mode 2: + Flight Mode 3: + Flight Mode 4: + Flight Mode 5: + Flight Mode 6: @@ -380,77 +473,92 @@ APMLightsComponent - + + Disabled - + + Channel 5 - + + Channel 6 - + + Channel 7 - + + Channel 8 - + + Channel 9 - + + Channel 10 - + + Channel 11 - + + Channel 12 - + + Channel 13 - + + Channel 14 - + + Light Output Channels - + + Lights 1: - + + Lights 2: - + + Brightness Steps: @@ -468,66 +576,79 @@ APMLightsComponentSummary + Disabled + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Lights Output 1: + Lights Output 2: @@ -536,6 +657,7 @@ APMNotSupported + Not supported @@ -544,81 +666,154 @@ APMPowerComponent - + + Power Module 90A - + + Power Module HV - + + + 3DR Iris + + + + + Other - + + + Measure battery voltage using an external voltmeter and enter the value below. Click Calculate to set the new voltage multiplier. + + + + + + Measured voltage: + + + + + + Vehicle voltage: + + + + + + Measure current draw using an external current meter and enter the value below. Click Calculate to set the new amps per volt value. + + + + + + Measured current: + + + + + + Vehicle current: + + + + + Battery monitor: - + + Battery capacity: - + + + Minimum arming voltage: + + + + + Power sensor: - + + Current pin: - + + Voltage pin: - + + + + Voltage multiplier: - - + + + + Calculate - + + Calculate Voltage Multiplier - + + If the battery voltage reported by the vehicle is largely different than the voltage read externally using a voltmeter you can adjust the voltage multiplier value to correct this. Click the Calculate button for help with calculating a new value. - + + + + Amps per volt: - + + Calculate Amps per Volt - + + If the current draw reported by the vehicle is largely different than the current read externally using a current meter you can adjust the amps per volt value to correct this. Click the Calculate button for help with calculating a new value. + + + Power + + The Power Component is used to setup battery parameters. @@ -628,11 +823,13 @@ APMPowerComponentSummary + Battery monitor: + Battery capacity: @@ -654,11 +851,16 @@ APMRadioComponentSummary + Roll: + + + + @@ -667,6 +869,10 @@ + + + + @@ -675,16 +881,19 @@ + Pitch: + Yaw: + Throttle: @@ -699,7 +908,7 @@ - Safety Setup is used to setup failsafe actions, geofence limits, leak detection, and arming checks. + Safety Setup is used to setup failsafe actions, leak detection, and arming checks. @@ -711,138 +920,167 @@ APMSafetyComponentCopter + Failsafe Triggers + Ground Station failsafe: + Throttle failsafe: + + Disabled + Always RTL + Continue with Mission in Auto Mode + Always Land + PWM threshold: + Battery failsafe: + Land + + Return to Launch + Voltage threshold: + MAH threshold: + GeoFence + Circle GeoFence enabled + Altitude GeoFence enabled + Report only + RTL or Land + Max radius: + Max altitude: + Return at current altitude + Return at specified altitude: + Loiter above Home for: + Land with descent speed: + Final loiter altitude: + Arming Checks + Warning: Turning off arming checks can lead to loss of Vehicle control. @@ -851,41 +1089,49 @@ APMSafetyComponentPlane + Failsafe Triggers + Throttle PWM threshold: + Voltage threshold: + MAH threshold: + GCS failsafe + Return to Launch + Return at current altitude + Return at specified altitude: @@ -894,60 +1140,137 @@ APMSafetyComponentRover - - Not supported + + + Failsafe Triggers + + + + + + Ground Station failsafe: + + + + + + Throttle failsafe: + + + + + + PWM threshold: + + + + + + Failsafe Crash Check: + + + + + + Disabled + + + + + + Hold + + + + + + Hold and Disarm + + + + + + Arming Checks + + + + + + Warning: Turning off arming checks can lead to loss of Vehicle control. APMSafetyComponentSub + Failsafe Actions + GCS Heartbeat: - + + Leak: - + + + Detector Pin: + + + + + Battery: - + + EKF: - + + Pilot Input: - + + Internal Temperature: - + + Internal Pressure: - + + + Threshold: + + + + + Arming Checks - + + Warning: Turning off arming checks can lead to loss of Vehicle control. @@ -955,6 +1278,9 @@ APMSafetyComponentSummaryCopter + + + @@ -962,21 +1288,27 @@ + Always RTL + Continue with Mission in Auto Mode + Always Land + + + @@ -984,93 +1316,113 @@ + + Land + Return to Launch + Arming Checks: + Enabled + Some disabled + Throttle failsafe: + Battery failsafe: + + GeoFence: + Altitude + Circle + Altitude,Circle + Report only + RTL or Land + RTL min alt: + current + RTL loiter time: + RTL final alt: + Descent speed: @@ -1079,11 +1431,15 @@ APMSafetyComponentSummaryPlane + Throttle failsafe: + + + @@ -1091,21 +1447,25 @@ + Voltage failsafe: + mAh failsafe: + RTL min alt: + current @@ -1114,60 +1474,143 @@ APMSafetyComponentSummaryRover - - Not supported + + + + + + + Disabled + + + + + + Always RTL + + + + + + Always Hold + + + + + + + + Unknown + + + + + + Hold + + + + + + Hold and Disarm + + + + + + Arming Checks: + + + + + + Enabled + + + + + + Some disabled + + + + + + Throttle failsafe: + + + + + + Failsafe Action: + + + + + + Failsafe Crash Check: APMSafetyComponentSummarySub - + + Arming Checks: - + + Enabled - + + Some disabled - + + GCS failsafe: - + + Leak failsafe: - + + Battery failsafe: - + + EKF failsafe: - + + Pilot Input failsafe: - + + Int. Temperature failsafe: - + + Int. Pressure failsafe: @@ -1175,50 +1618,183 @@ APMSensorsComponent - - + + + If the orientation is in the direction of flight, select None. + + + + + + Before calibrating make sure orientation settings are correct. + + + + + + If the compass or GPS module is mounted in flight direction, leave the default value (None) + + + + + + For Compass calibration you will need to rotate your vehicle through a number of positions. + + + + + + For Gyroscope calibration you will need to place your vehicle on a surface and leave it still. + + + + + + For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds. + + + + + + To level the horizon you need to place the vehicle in its level flight position and press OK. + + + + + + Start the individual calibration steps by clicking one of the buttons to the left. + + + + + + The calibration for Compass %1 appears to be poor. + + + + + + Check the compass position within your vehicle and re-do the calibration. + + + + + + + Calibrate Compass - + + Calibrate Accelerometer - - + + + + Sensor Settings - + + Calibration Cancel - + + Waiting for Vehicle to response to Cancel. This may take a few seconds. - - - + + + + + + Calibration complete - + + Accelerometer calibration complete. - + + Compass calibration complete. + + + + Sensor Calibration + + + + + + Performing sensor calibration over a WiFi connection can be unreliable. If you run into problems try using a direct USB connection instead. + + + + + + + + Compass + + + + + + + + (primary + + + + + + + + (secondary + + + + + + + + , external + + + + + + + + , internal + + + + + + + + Use Compass + + + Shown in the indicator bars is the quality of the calibration for each compass. @@ -1226,18 +1802,21 @@ + - Green indicates a well functioning compass. + - Yellow indicates a questionable compass or calibration. + - Red indicates a compass which should not be used. @@ -1245,97 +1824,196 @@ + YOU MUST REBOOT YOUR VEHICLE AFTER EACH CALIBRATION. + Orientation: + Autopilot Orientation: + + + This is recommended for vehicles that have only an internal compass and on vehicles where there is significant interference on the compass from the motors, power wires, etc. + + + + + + CompassMot only works well if you have a battery current monitor because the magnetic interference is linear with current drawn. + + + + + + It is technically possible to set-up CompassMot using throttle but this is not recommended. + + + + + + Disconnect your props, flip them over and rotate them one position around the frame. + + + + + + In this configuration they should push the copter down into the ground when the throttle is raised. + + + + + + Secure the copter (perhaps with tape) so that it does not move. + + + + + + Turn on your transmitter and keep throttle at zero. + + + + + + Click Ok to start CompassMot calibration. + + + + To level the horizon you need to place the vehicle in its level flight position and press Ok. - - Pressure calibration will set the depth to zero at the current pressure reading. + + + depth + + + + + + altitude + + + + + + Pressure calibration will set the %1 to zero at the current pressure reading. %2 - + + + To calibrate the airspeed sensor shield it from the wind. Do not touch the sensor or obstruct any holes during the calibration. + + + + + Accelerometer - + + Compass - + + Accelerometer must be calibrated prior to Compass. - + + Level Horizon - + + Accelerometer must be calibrated prior to Level Horizon. - + + Calibrate Pressure - + + + Cal Baro/Airspeed + + + + + CompassMot + CompassMot - Compass Motor Interference Calibration - + + Next - + + Cancel - - - - - - + + + + + + + + + + + + Rotate - - - - - - + + + + + + + + + + + + Hold Still @@ -1353,87 +2031,112 @@ APMSensorsComponentController - + + Calibration complete + + + + + Calibration failed. Calibration log will be displayed. + + + + Rotate the vehicle randomly around all axes until the progress bar fills all the way to the right . - + Raise the throttle slowly to between 50% ~ 75% (the props will spin!) for 5 ~ 10 seconds. - + Quickly bring the throttle back down to zero - + Press the Next button to complete the calibration - + Hold the vehicle in its level flight position. - + Requesting pressure calibration... - + + Rotate the vehicle continuously as shown in the diagram until marked as Completed + + + + + Hold still in the current orientation + + + + + Place you vehicle into one of the orientations shown below and hold it still + + + + Level horizon complete - + Level horizon failed - + Pressure calibration success - + Pressure calibration fail - + Compass %1 calibration complete - + Compass %1 calibration below quality threshold - + All compasses calibrated successfully - + YOU MUST REBOOT YOUR VEHICLE NOW FOR NEW SETTINGS TO TAKE AFFECT - + Compass calibration failed - + YOU MUST REBOOT YOUR VEHICLE NOW AND RETRY COMPASS CALIBRATION PRIOR TO FLIGHT - + Continue rotating... @@ -1441,27 +2144,33 @@ APMSensorsComponentSummary + Compass + + Setup required + Not installed + Accelerometer(s): + Ready @@ -1482,13 +2191,16 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - - + + + + Load Vehicle Default Parameters - + + Select your vehicle to load the default parameters: @@ -1496,22 +2208,27 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t APMSubFrameComponentSummary + Frame Type: + Firmware Version: + + Unknown + Git Revision: @@ -1519,6 +2236,11 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t APMTuningComponent + + + Tuning + + Tuning Setup is used to tune the flight characteristics of the Vehicle. @@ -1528,116 +2250,139 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t APMTuningComponentCopter + Basic Tuning + Throttle Hover + How much throttle is needed to maintain a steady hover + Roll/Pitch Sensitivity + Slide to the right if the copter is sluggish or slide to the left if the copter is twitchy + Climb Sensitivity + Slide to the right to climb more aggressively or slide to the left to climb more gently + RC Roll/Pitch Feel + Slide to the left for soft control, slide to the right for crisp control + AutoTune + Axes to AutoTune: + Channel for AutoTune switch: + None + Channel 7 + Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + In Flight Tuning + Channel Option 6 (Tuning): + Min: + Max: @@ -1646,16 +2391,19 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t AirframeComponent + Custom Airframe Config + Your vehicle is using a custom airframe configuration. + This configuration can only be modified through the Parameter Editor. @@ -1663,31 +2411,38 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + If you want to reset your airframe configuration and select a standard configuration, click 'Reset' above. + Clicking “Apply” will save the changes you have made to your airframe configuration.<br><br>All vehicle parameters other than Radio Calibration will be reset.<br><br>Your vehicle will also be restarted in order to complete the process. + You've connected a %1. + Airframe is not set. + To change this configuration, select the desired airframe below then click “Apply and Restart”. + + Apply and Restart @@ -1704,35 +2459,50 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + AirframeComponentController + + + You cannot change airframe configuration while connected to multiple vehicles. + + + AirframeComponentSummary + System ID: + Airframe type: + + Setup required + Vehicle: + Firmware Version: + Unknown @@ -1764,37 +2534,37 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t AppMessages - + Log files (*.txt) - + All Files (*) - + Select log save file - + Save App Log - + Show Latest - + Set logging - + Turn on logging categories @@ -1808,28 +2578,36 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - ArduCopterFirmwarePlugin + ArmedIndicator - - Unable to takeoff: Vehicle failed to change to Guided mode. + + Armed - - Unable to takeoff: Vehicle failed to arm. + + Disarmed - ArmedIndicator + AudioOutput - - Armed + + negative - - Disarmed + + meters + + + + + AutoPilotPlugin + + + One or more vehicle components require setup prior to flight. @@ -1851,6 +2629,14 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + BluetoothLink + + + Bluetooth Link Error + + + BluetoothSettings @@ -1911,6 +2697,11 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Read failed: error: %1 + + + Get Command Response: + + Invalid sync response: 0x%1 0x%2 @@ -1931,6 +2722,17 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Command failed: 0x%1 (%2) + + + + Get Board Info: + + + + + Send Command: + + Board erase failed: %1 @@ -1950,9 +2752,21 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + Flash failed: %1 at address 0x%2 + + + + Unable to retrieve block from ihx: index %1 + + + + + Unable to set flash start address: 0x%2 + + @@ -1961,9 +2775,15 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + Compare failed: expected(0x%1) actual(0x%2) at address: 0x%3 + + + Unable to set read start address: 0x%2 + + CRC mismatch: board(0x%1) file(0x%2) @@ -1979,70 +2799,161 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Found unsupported bootloader version: %1 + + + Get Board Id: + + + + + CameraCalc + + + Camera + + + + + Width + + + + + Height + + + + + Sensor + + + + + Image + + + + + Focal length + + + + + Front Lap + + + + + Side Lap + + + + + Overlap + + + + + Select one: + + + + + Image density + + + + + CameraCalc section version %1 not supported + + + + + Custom Camera + + + + + Manual (no camera specs) + + CameraComponent + Vehicle must be restarted for changes to take effect. + Apply and Restart + Camera Trigger Settings + Trigger mode + Trigger interface + Time Interval + Distance Interval + Hardware Settings + AUX Pin Assignment + Trigger Pin Polarity + Trigger Period + Camera Test + Trigger Camera @@ -2061,36 +2972,130 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t CameraComponentSummary + Trigger interface: + Trigger mode: + Time interval: + Distance interval: + AUX pins: + AUX pin polarity: + + CameraPageWidget + + + Video Settings + + + + + Camera Settings + + + + + Trigger Camera + + + + + Camera + + + + + Free Space: + + + + + Single + + + + + Time Lapse + + + + + Photo Mode + + + + + Photo Interval (seconds) + + + + + Reset Camera Defaults + + + + + Reset + + + + + Reset Camera to Factory Settings + + + + + Confirm resetting all settings? + + + + + Storage + + + + + Format + + + + + Format Camera Storage + + + + + Confirm erasing all files? + + + CameraSection @@ -2129,53 +3134,40 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - - CameraWidget - - - Camera Controls - - - - - Trigger Camera - - - CenterMapDropButton - + Center map on: - + Mission - + All items - + Home - + Current Location - + Vehicle - + Follow Vehicle @@ -2213,19 +3205,123 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + CorridorScanComplexItem + + + %1 does not support loading this complex mission item type: %2:%3 + + + + + %1 complex item version %2 not supported + + + + + CorridorScanEditor + + + WARNING: Photo interval is below minimum interval (%1 secs) supported by camera. + + + + + Altitude + + + + + Trigger Distance + + + + + Spacing + + + + + Corridor + + + + + Width + + + + + Turnaround dist + + + + + Take images in turnarounds + + + + + Relative altitude + + + + + Rotate Entry Point + + + + + Statistics + + + + + Photo count + + + + + Photo interval + + + + + secs + + + CustomCommandWidget - + + No vehicle connected + + + + Load Custom Qml file... - + Reset + + CustomCommandWidgetController + + + Select custom Qml file + + + + + Qml files (*.qml) + + + DebugWindow @@ -2506,48 +3602,155 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + EditPositionDialog + + + Latitude + + + + + Longitude + + + + + Set Geographic + + + + + Zone + + + + + Hemisphere + + + + + Easting + + + + + Northing + + + + + Set UTM + + + FWLandingPatternEditor - + Loiter point - - + + + Altitude + + + + + Radius + + + + + Altitude relative to home - + Loiter clockwise - + Landing point - - WIP (NOT FOR REAL FLIGHT!) + + Heading + + + + + Landing dist - + + Descent rate + + + + Click in map to set landing point. + + Fact + + + Unknown: %1 + + + FactCheckBox - + Label + + FactMetaData + + + Other + + + + + Misc + + + + + + + + + + + + + + + + Value must be within %1 and %2 + + + + + + Invalid number + + + FactPanel @@ -2556,6 +3759,19 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + FactPanelController + + + Incorrect FactPanel Qml implementation. FactPanelController used without passing in factPanel. + + + + + Internal Error: %1 + + + FactTextField @@ -2572,299 +3788,453 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t FileManager - + Unable to open local file for writing (%1) - + Unable to write data to local file (%1) - + Download: Incorrect session returned - + Download: Offset returned (%1) differs from offset requested/expected (%2) - + List: Offset returned (%1) differs from offset requested (%2) - + Incorrectly formed list entry: '%1' - + Missing NULL termination in list entry - + Write: Incorrect session returned - + Write: Offset returned (%1) differs from offset requested (%2) - + Write: Returned invalid size of write size data - + Write: Size returned (%1) differs from size requested (%2) - + Bad sequence number on received message: expected(%1) received(%2) - + Nak received creating file, error: %1 - + + Nak received creating directory, error: %1 + + + + Nak received, error: %1 - + Unknown opcode returned from server: %1 - - - + + + Command not sent. Waiting for previous command to complete. - - - - + + + + Command not sent. No Vehicle links. - - UAS File manager busy. Try again later + + + UAS File manager busy. Try again later - + File (%1) is not readable for upload - + Unable to open local file for upload (%1) - + Unable to read data from local file (%1) - - + + Timeout waiting for ack: Download failed - - + + Timeout waiting for ack: Upload failed + + FirmwareImage + + + Incorrectly formatted line in .ihx file, line too short + + + + + Unsupported record type in file: %1 + + + + + Unable to open firmware file %1, error: %2 + + + + + Supplied file is not a valid JSON document + + + + + Firmware file mission required key: %1 + + + + + Firmware file has invalid key: %1 + + + + + Downloaded firmware board id does not match hardware board id: %1 != %2 + + + + + Write failed for parameter meta data file, error: %1 + + + + + Unable to open parameter meta data file %1 for writing, error: %2 + + + + + Write failed for airframe meta data file, error: %1 + + + + + Unable to open airframe meta data file %1 for writing, error: %2 + + + + + Unable to open decompressed file %1 for writing, error: %2 + + + + + Write failed for decompressed image file, error: %1 + + + + + Firmware file has invalid decompressed size for %1 + + + + + Could not find compressed bytes for %1 in Firmware file + + + + + Incorrectly formed compressed bytes section for %1 in Firmware file + + + + + Firmware file has 0 length %1 + + + + + Size for decompressed %1 does not match stored size: Expected(%1) Actual(%2) + + + + + Successfully decompressed %1 + + + + + Unabled to open firmware file %1, %2 + + + FirmwarePlugin - - Sony ILCE-QX1 + + Sony NEX-5R 20mm + Sony ILCE-QX1 + + + + Canon S100 PowerShot - + Canon G9 X PowerShot - + Canon SX260 HS PowerShot - + Canon EOS-M 22mm - + Sony a6000 16mm + + + Sony RX100 II 28mm + + + + + Ricoh GR II + + + + + RedEdge + + + + + Parrot Sequioa RGB + + + + + Parrot Sequioa Monochrome + + + + + GoPro Hero 4 + + FirmwareUpgrade - + %1 can upgrade the firmware on Pixhawk devices, SiK Radios and PX4 Flow Smart Cameras. - + + Update the autopilot firmware to the latest version + + + + All %1 connections to vehicles must be - + Upgrade cancelled - + Found device - - + + PX4 Flight Stack - - + + Standard Version (stable) - + Beta Testing (beta) - + Developer Build (master) - - - + + + Custom firmware file... - + Standard Version - + Detected PX4 Flow board. You can select from the following firmware: - + Detected Pixhawk board. You can select from the following flight stacks: - + Press Ok to upgrade your vehicle. - + ArduPilot Flight Stack - + Advanced settings - + Select which version of the firmware you would like to install: - + Select which version of the above flight stack you would like to install: - + + Select the standard version or one from the file system (previously downloaded): + + + + WARNING: BETA FIRMWARE. - + This firmware version is ONLY intended for beta testers. - + Although it has received FLIGHT TESTING, it represents actively changed code. - + Do NOT use for normal operation. - + WARNING: CONTINUOUS BUILD FIRMWARE. - + This firmware has NOT BEEN FLIGHT TESTED. - + It is only intended for DEVELOPERS. - + Run bench tests without props first. - + Do NOT fly this without additional safety precautions. - + Follow the mailing list actively when using it. @@ -2876,64 +4246,139 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Connect not allowed during Firmware Upgrade. - - - FixedWingLandingComplexItem - - %1 does not support loading this complex mission item type: %2:%3 + + Connected to bootloader: + + + + + Version: %1 + + + + + Board ID: %1 + + + + + Flash size: %1 + + + + + Attempting to flash an unknown board type, you must select 'Custom firmware file' + + + + + Select Firmware File + + + + + Firmware Files (*.px4 *.bin *.ihx) + + + + + Unable to find specified firmware download location + + + + + No firmware file selected + + + + + Downloading firmware... + + + + + From: %1 + + + + + Download complete + + + + + Image load failed + + + + + Bootloader not found + + + + + Image size of %1 is too large for board flash size %2 - - - FlightDisplayView - - Joystick support requires MAVLink MANUAL_CONTROL support. + + Upgrade complete - - The firmware you are running does not normally support this. + + Upgrade cancelled + + + FixedWingLandingComplexItem - - It will only work if you have modified the firmware to add MANUAL_CONTROL support. + + %1 does not support loading this complex mission item type: %2:%3 + + + FlightDisplayView - + Flight Plan complete - + %1 Images Taken - + Remove plan from vehicle - + + Leave plan on vehicle + + + + Single - - Multi-Vehicle (WIP) + + Multi-Vehicle - + Fly - + Action @@ -2941,14 +4386,14 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t FlightDisplayViewMap - + R rally point map item label - - G + + Goto here Goto here waypoint @@ -2956,15 +4401,20 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t FlightDisplayViewVideo - + WAITING FOR VIDEO + + + VIDEO DISABLED + + FlightDisplayViewWidgets - + No GPS Lock for Vehicle @@ -3003,28 +4453,37 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t FlightModesComponentSummary + + Mode switch: + + Setup required + Flight Mode %1 : + Position Ctl switch: + + + @@ -3032,24 +4491,18 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + Loiter switch: + Return switch: - - Form - - - Form - - - GPSIndicator @@ -3104,225 +4557,298 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - GeneralSettings + GPSRTKIndicator - - (Requires Restart) + + Survey-in Active - - Units (Requires Restart) + + RTK Streaming - - Distance: + + Duration: - + + Accuracy: + + + + + Current Accuracy: + + + + + Satellites: + + + + + GeneralSettings + + + (Requires Restart) + + + + + Units (Requires Restart) + + + + + Distance: + + + + Area: - + Speed: - + Miscellaneous - + Map Provider: - + Map Type: - + Mute all audio output - + Save telemetry log after each flight - + Save telemetry log even if vehicle was not armed - + Clear all settings on next start - + Clear Settings - + All saved settings will be reset the next time you start %1. Is this really what you want? - + Announce battery lower than: - + Virtual Joystick - + Font Size: - + + Temperature: + + + + Color Scheme: - + Default Mission Altitude: - + AutoLoad Missions - + File Save Path: - - + <not set> - + Choose the location to save files: - + RTK GPS (Requires Restart) - + Survey in accuracy: - + Minimum observation duration: - + AutoConnect to the following devices: - + Pixhawk - + SiK Radio - + PX4 Flow - + LibrePilot - + UDP - + RTK GPS - + + NMEA GPS Device: + + + + + NMEA GPS Baudrate: + + + + Video - + Video Source: - + UDP Port: - + RTSP URL: - + + TCP URL: + + + + Aspect Ratio: - - Grid Lines: + + Disable When Disarmed: + + + + + Brand Image + + + + + Indoor Brand Image Path: + + + + + + Choose custom brand image file: + + + + + Outdoor Brand Image Path: - + Video Recording - - Max Storage Usage: + + Auto-Delete Files: - - Video File Format: + + Max Storage Usage: - - Save Path: + + Video File Format: - + %1 Version: %2 @@ -3330,7 +4856,7 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GeoFenceController - + GeoFence: %1 @@ -3338,23 +4864,95 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GeoFenceEditor - + GeoFence - + GeoFencing allows you to set a virtual ‘fence’ around the area you want to fly in. - - Add fence polygon + + This vehicle does not support GeoFence. + + + + + Insert GeoFence + + + + + Polygon Fence - - Remove fence polygon + + Circular Fence + + + + + Polygon Fences + + + + + + None + + + + + + Inclusion + + + + + + Edit + + + + + + Delete + + + + + Circular Fences + + + + + Radius + + + + + GeoFenceManager + + + GeoFence load: Vertex count change mid-polygon - actual:expected + + + + + GeoFence load: Polygon type changed before last load complete - actual:expected + + + + + GeoFence load: Incomplete polygon loaded + + + + + GeoFence load: Unsupported command %1 @@ -3527,7 +5125,7 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GuidedActionConfirm - + Slide to confirm @@ -3535,7 +5133,7 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GuidedActionList - + Select Action @@ -3543,167 +5141,192 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GuidedActionsController - + Emergency Stop - + Arm - + Disarm - + RTL - + Takeoff - + Land - + Start Mission - + Continue Mission - + Resume Mission - + + Resume FAILED + + + + Pause - + Change Altitude - + Orbit - + Land Abort - + Set Waypoint - + Goto Location - + + VTOL Transition + + + + Arm the vehicle. - + Disarm the vehicle - + WARNING: This will stop all motors. If vehicle is currently in air it will crash. - + Takeoff from ground and hold position. - + Takeoff from ground and start the current mission. - + Continue the mission from the current waypoint. - + Resume the current mission. This will re-generate the mission from waypoint %1, takeoff and continue the mission. - + + Upload of resume mission failed. Confirm to retry upload + + + + Review the modified mission. Confirm if you want to takeoff and begin mission. - + Land the vehicle at the current position. - + Return to the home position of the vehicle. - + Change the altitude of the vehicle up or down. - + Move the vehicle to the location clicked on the map. - + Adjust current waypoint to %1. - + Orbit the vehicle around the current location. - + Abort the landing sequence. - + Pause the vehicle at it's current position. - - _activeVehicle(%1) _vehicleArmed(%2) guidedModeSupported(%3) _vehicleFlying(%4) _vehicleInRTLMode(%5) pauseVehicleSupported(%6) _vehiclePaused(%7) _flightMode(%8) + + Pause all vehicles at their current position. + + + + + Transition VTOL to fixed wing flight. - + + Transition VTOL to multi-rotor flight. + + + + Internal error: unknown actionCode @@ -3711,11 +5334,19 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t GuidedAltitudeSlider - + New Alt(rel) + + HealthPageWidget + + + All systems healthy + + + JoystickConfig @@ -3729,156 +5360,201 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - + Not Mapped - + Attitude Controls - + Lateral - + Roll - + Forward - + Pitch - + Yaw - + Throttle - + Skip - + Cancel - + Calibrate - + Additional Joystick settings: - + Enable joystick input - + Enable not allowed (Calibrate First) - + Active joystick: - + Active joystick name not in combo - + Center stick is zero throttle - + Spring loaded throttle smoothing - + Full down stick is zero throttle - + + Allow negative Thrust + + + + Exponential: - + Advanced settings (careful!) - + Joystick mode: - + Deadbands - - Button actions: + + Deadband can be set during the first + + + + + step of calibration by gently wiggling each axis. + Deadband can also be adjusted by clicking and + + + + + dragging vertically on the corresponding axis monitor. + + + + + Button actions: + + + + Buttons 0-%1 reserved for firmware use - + # - + Function: - + Shift Function: - + Axis Monitor - + Button Monitor + + JoystickConfigController + + + Detected %1 joystick axes. To operate PX4, you need at least %2 axes. + + + + + Calibrate + + + + + The current calibration settings are now displayed for each axis on screen. + +Click the Next button to upload calibration to board. Click Cancel if you don't want to save these values. + + + JoystickIndicator @@ -3963,6 +5639,66 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Overwrite timestamp of data from vehicle with ground receive time. Helps if the plots are not visible because of missing or invalid onboard time. + + + Time axis: + + + + + 10 seconds + + + + + 20 seconds + + + + + 30 seconds + + + + + 40 seconds + + + + + 50 seconds + + + + + 1 minute + + + + + 2 minutes + + + + + 3 minutes + + + + + 4 minutes + + + + + 5 minutes + + + + + 10 minutes + + No curves selected for logging. @@ -4037,20 +5773,30 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t LinkManager - - - - + + Connect not allowed: %1 + + + + + + + %1 on %2 (AutoConnect) - + + Shutdown + + + + Please check to make sure you have an SD Card inserted in your Vehicle and try again. - + Your Vehicle is not responding. If this continues, shutdown %1, restart the Vehicle letting it boot completely, then start %1. @@ -4149,43 +5895,48 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t LogDownloadController - + Available - - + + Canceled - - - + + + Error - + Downloaded - + Timed Out - + Log Download Directory - + Waiting + + + UnknownDate + + LogDownloadPage @@ -4272,11 +6023,43 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t LogReplayLink + + + Log Replay Error + + + + + You must close all connections prior to replaying a log. + + + + + Attempt to load new log while log being played + + + + + Unable to open log file: '%1', error: %2 + + + + + The log file '%1' is corrupt. No valid timestamps were found at the end of the file. + + Connect not allowed during Flight Data replay. + + + + + Unable to seek to new position + + LogReplaySettings @@ -4304,29 +6087,29 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MAVLinkProtocol - - - + + + MAVLink Protocol - - There is a MAVLink Version or Baud Rate Mismatch. Please check if the baud rates of %1 and your autopilot are the same. + + MAVLink Logging failed. Could not write to file %1, logging disabled. - - MAVLink Logging failed. Could not write to file %1, logging disabled. + + Detected radio still using MAVLink v1.0 on a link with MAVLink v2.0 enabled. Please upgrade the radio firmware. - + MAVLink protocol - + Opening Flight Data file for writing failed. Unable to write to %1. Please choose a different file location. @@ -4344,12 +6127,12 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - + Disconnect - + COMMUNICATION LOST @@ -4397,7 +6180,7 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - + Setting up user interface @@ -4436,23 +6219,23 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MainWindowInner - - + + %1 close - + You have a mission edit in progress which has not been saved/sent. If you close you will lose changes. Are you sure you want to close? - + There are still active connections to vehicles. Do you want to disconnect these before closing? - + No Messages @@ -4497,6 +6280,11 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t Mavlink Console provides a connection to the vehicle's system shell. + + + Show Latest + + MavlinkSettings @@ -4695,39 +6483,59 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MissionController - + Survey - + Fixed Wing Landing - + + Structure Scan + + + + + Corridor Scan + + + + Mission item %1 is not an object - + Unsupported complex item type: %1 - + Unknown item type: %1 - + Could not find doJumpId: %1 - - - + + The mission file is corrupted. + + + + + The mission file is not compatible with this version of %1. + + + + + + Mission: %1 @@ -4735,17 +6543,17 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MissionItem - + Type found: %1 must be: %2 - - %1 key must contains 4 values + + %1 key must contains 7 values - + Param %1 incorrect type %2, must be double or null @@ -4753,47 +6561,57 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MissionItemEditor - + Insert waypoint - + Insert pattern - + Insert - + Delete - + Change command... - + + Edit position... + + + + + Edit Position + + + + Show all values - + Mission Edit - + You have made changes to the mission item which cannot be shown in Simple Mode - + Select Mission Command @@ -4801,15 +6619,15 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MissionItemStatus - - Altitude + + Terrain Altitude MissionManager - + Unable to generate resume mission due to MAV_CMD_DO_JUMP command. @@ -4827,57 +6645,62 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t - - Mission Defaults + + Waypoint alt - - Waypoint alt + + Flight speed - - Flight speed + + Above camera commands will take affect immediately upon mission start. - - RTL after mission end + + Mission End - + + Return To Launch + + + + Vehicle Info - + Cruise speed - + Hover speed - + Planned Home Position - + Altitude - + Actual position set by vehicle at flight time. - + Set Home To Map Center @@ -4923,42 +6746,47 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t MockLinkSettings - + Mock Link Settings - + Send Status Text and Voice - + + High latency + + + + PX4 Firmware - + APM Firmware - + Generic Firmware - + APM Vehicle Type - + ArduCopter - + ArduPlane @@ -5034,10 +6862,33 @@ propellers on the green thrusters and counter-clockwise propellers on the blue t + + MultiVehicleList + + + The following commands will be applied to all vehicles + + + + + Armed + + + + + Disarmed + + + MultiVehicleManager - + + Warning: A vehicle is using the same system id as %1: %2 + + + + Connected to Vehicle %1 @@ -5099,199 +6950,201 @@ Is this really what you want? - + System Wide Tile Cache - + Zoom Levels: - + Total: - + Unique: - + Downloaded: - + Error Count: - + Size: - - + + Tile Count: - + Resume Download - + Cancel Download - + Delete - + Confirm Delete - - - + + Ok + + + + + + Close - + Min Zoom: %1 - + Max Zoom: %1 - + + Add New Set - + Name: - + Map type: - + Min/Max Zoom Levels - + Est Size: - + Too many tiles - + Download - - - + + + + Cancel - - Add new set - - - - - + + Import - - + + Export - + Options - + Offline Maps Options - + Select Tile Sets to Export - + Select All - + Select None - + Tile Set Export Progress - + Tile Set Export Completed - + Map Tile Set Import - + Map Tile Set Import Progress - + Map Tile Set Import Completed - + Append to existing set - + Replace existing set @@ -5299,347 +7152,486 @@ Is this really what you want? PX4AdvancedFlightModes + FLIGHT MODES + Assign Flight Modes to radio control channels and adjust the thresholds for triggering them. + Assign Flight Modes to radio control channels and adjust the thresholds for triggering them. + You can assign multiple flight modes to a single channel. + Turn your radio control on to test switch settings. + The following channels: + are not available for Flight Modes since they are already in use for other functions. + Manual/Main + Stabilized/Main + The pilot has full control of the aircraft, no assistance is provided. + + The Main mode switch must always be assigned to a channel in order to fly + The pilot has full control of the aircraft, only attitude is stabilized. + Assist + If Position Control is placed on a separate channel from the Main mode channel, an additional 'Assist' mode is added to the Main switch. + In order for the Attitude Control/Position Control switch to be active, the Main switch must be in Assist mode. + Auto + If Loiter is placed on a separate channel from the Main mode channel, an additional 'Auto' mode is added to the Main switch. + In order for the Mission/Loiter switch to be active, the Main switch must be in Auto mode. + Stabilized + Acro + Roll/pitch angles and rudder deflection are controlled. + The angular rates are controlled, but not the attitude. + Altitude + Roll stick controls banking, pitch stick altitude + Throttle stick controls speed. + With no stick inputs the plane holds heading, but drifts off in wind. + Same as Stablized mode except that Throttle controls climb/sink rate. Centered Throttle holds altitude steady. + Position Control + Roll stick controls banking, pitch stick controls altitude. + Throttle stick controls speed. + With no stick inputs the plane flies a straight line, even in wind. + Roll and Pitch sticks control sideways and forward speed + Throttle stick controls climb / sink rade. + Mission + The aircraft obeys the programmed mission sent by QGroundControl. + Hold + The aircraft flies in a circle around the current position at the current altitude. + The multirotor hovers at the current position and altitude. + Return + The vehicle returns to the home position, loiters and then lands. + Offboard + All flight control aspects are controlled by an offboard system. + Flight Mode Config is disabled since you have a Joystick enabled. + Use Single Channel Mode Selection + Generate Thresholds + + PX4AdvancedFlightModesController + + + %1 is set to %2. Mapping must between 0 and %3 (inclusive). + + + + + + %1 is set to same channel as %2. + + + + + + %1 is set to %2. Threshold must between 0.0 and 1.0 (inclusive). + + + + + + PX4AutoPilotPlugin + + + This version of GroundControl can only perform vehicle setup on a newer version of firmware. Please perform a Firmware Upgrade if you wish to use Vehicle Setup. + + + PX4FirmwarePlugin - + Manual - + Acro - + Stabilized - + Rattitude - + Altitude - + Position - + Offboard - + Ready - + Takeoff - + Hold - + Mission - + Return - + Land - + Return to Groundstation - + Follow Me - + Simple - + Unknown %1:%2 - + Unable to takeoff, vehicle position not known. - - Unable to takeoff, MIS_TAKEOFF_ALT parameter missing. + + Unable to start mission: Vehicle rejected arming. + + + + + Unable to start mission: Vehicle not ready. + + + + + QGroundControl supports PX4 Pro firmware Version %1.%2.%3 and above. You are using a version prior to that which will lead to unpredictable results. Please upgrade your firmware. - + Unable to go to location, vehicle position not known. - + Unable to change altitude, home position unknown. - + Unable to change altitude, home position altitude unknown. + + + PX4FirmwareUpgradeThreadWorker + + + Putting radio into command mode + + - - Unable to start mission: Vehicle failed to arm. + + Unable to open port: %1 error: %2 + + + + + + Unable to put radio into command mode + + + + + Rebooting radio to bootloader + + + + + Unable to reboot radio (bytes written) + + + + + Unable to reboot radio (ready read) + + + + + Programming new version... + + + + + Verifying program... + + + + + Verify complete + + + + + Erasing previous program... + + + + + Erase complete @@ -5654,12 +7646,12 @@ Is this really what you want? PX4ParameterMetaData - + Enabled - + Disabled @@ -5680,11 +7672,16 @@ Is this really what you want? PX4RadioComponentSummary + Roll: + + + + @@ -5693,26 +7690,33 @@ Is this really what you want? + Pitch: + Yaw: + Throttle: + Flaps: + + + @@ -5720,11 +7724,13 @@ Is this really what you want? + Aux1: + Aux2: @@ -5733,28 +7739,61 @@ Is this really what you want? PX4SimpleFlightModes + Flight Mode Settings + Mode channel: + Flight Mode %1 + Switch Settings + + + + Return switch: + + + + + + Kill switch: + + + + + + Offboard switch: + + + + + + VTOL mode switch: + + PX4TuningComponent + + + Tuning + + Tuning Setup is used to tune the flight characteristics of the Vehicle. @@ -5764,165 +7803,200 @@ Is this really what you want? PX4TuningComponentCopter + Hover Throttle + Adjust throttle so hover is at mid-throttle. Slide to the left if hover is lower than throttle center. Slide to the right if hover is higher than throttle center. + Manual minimum throttle + Slide to the left to start the motors with less idle power. Slide to the right if descending in manual flight becomes unstable. + Roll sensitivity + Slide to the left to make roll control faster and more accurate. Slide to the right if roll oscillates or is too twitchy. + Pitch sensitivity + Slide to the left to make pitch control faster and more accurate. Slide to the right if pitch oscillates or is too twitchy. + + + PX4TuningComponentPlane - - Altitude control sensitivity + + + Roll sensitivity - - Slide to the left to make altitude control smoother and less twitchy. Slide to the right to make altitude control more accurate and more aggressive. + + + Slide to the left to make roll control faster and more accurate. Slide to the right if roll oscillates or is too twitchy. - - Position control sensitivity + + + Pitch sensitivity - - Slide to the left to make flight in position control mode smoother and less twitchy. Slide to the right to make position control more accurate and more aggressive. + + + Slide to the left to make pitch control faster and more accurate. Slide to the right if pitch oscillates or is too twitchy. - - - PX4TuningComponentVTOL - - Hover Roll sensitivity + + + Cruise throttle - - Slide to the left to make roll control during hover faster and more accurate. Slide to the right if roll oscillates or is too twitchy. + + + This is the throttle setting required to achieve the desired cruise speed. Most planes need 50-60%. - - Hover Pitch sensitivity + + + Mission mode sensitivity - - Slide to the left to make pitch control during hover faster and more accurate. Slide to the right if pitch oscillates or is too twitchy. + + + Slide to the left to make position control more accurate and more aggressive. Slide to the right to make flight in mission mode smoother and less twitchy. + + + PX4TuningComponentVTOL - - Hover Altitude control sensitivity + + + Hover Roll sensitivity - - Slide to the left to make altitude control during hover smoother and less twitchy. Slide to the right to make altitude control more accurate and more aggressive. + + + Slide to the left to make roll control during hover faster and more accurate. Slide to the right if roll oscillates or is too twitchy. - - Hover Position control sensitivity + + + Hover Pitch sensitivity - - Slide to the left to make flight during hover in position control mode smoother and less twitchy. Slide to the right to make position control more accurate and more aggressive. + + + Slide to the left to make pitch control during hover faster and more accurate. Slide to the right if pitch oscillates or is too twitchy. - + + Plane Roll sensitivity - + + Slide to the left to make roll control faster and more accurate. Slide to the right if roll oscillates or is too twitchy. - + + Plane Pitch sensitivity - + + Slide to the left to make pitch control faster and more accurate. Slide to the right if pitch oscillates or is too twitchy. - + + Plane Cruise throttle - + + This is the throttle setting required to achieve the desired cruise speed. Most planes need 50-60%. - + + Hover Throttle - + + Adjust throttle so hover is at mid-throttle. Slide to the left if hover is lower than throttle center. Slide to the right if hover is higher than throttle center. - + + Hoever manual minimum throttle - + + Slide to the left to start the motors with less idle power. Slide to the right if descending in manual flight becomes unstable. - + + Plane Mission mode sensitivity - + + Slide to the left to make position control more accurate and more aggressive. Slide to the right to make flight in mission mode smoother and less twitchy. @@ -5930,158 +8004,166 @@ Is this really what you want? ParameterEditor - + Parameter Load Errors - + Search: - + Clear - + Tools - + Refresh - + Reset all to defaults - + Reset All - + Load from file... - + Select Parameter File - - + + Parameter Files (*.%1) - - + + All Files (*.*) - + Save to file... - + Save Parameters - + Clear RC to Param + - Reboot Vehicle - - Component #: %1 - - - - + Parameter Editor - + Select Reset to reset all parameters to their defaults. - + Select Ok to reboot vehicle. + + ParameterEditorController + + + Unable to create file: %1 + + + + + Unable to open file: %1 + + + ParameterEditorDialog - + Reset to default - + Min: - + Max: - + Default: - + Parameter name: - + Warning: Modifying values while vehicle is in flight can lead to vehicle instability and possible vehicle loss. - + Make sure you know what you are doing and double-check your values before Save! - + Force save (dangerous!) - + Advanced settings - + Manual Entry - + Set RC to Param... @@ -6089,130 +8171,316 @@ Is this really what you want? ParameterManager - + + Change of parameter %1 requires a Vehicle reboot to take effect + + + + Parameter write failed: veh:%1 comp:%2 param:%3 - + Parameter read failed: veh:%1 comp:%2 param:%3 - + %1 was unable to retrieve the full set of parameters from vehicle %2. This will cause %1 to be unable to display its full user interface. If you are using modified firmware, you may need to resolve any vehicle startup errors to resolve the issue. If you are using standard firmware, you may need to upgrade to a newer version to resolve the issue. - + Vehicle %1 did not respond to request for parameters. This will cause %2 to be unable to display its full user interface. - + %1 key is not a json object + + PlanManager + + + Internal error occurred during Mission Item communication: _ackTimeOut:_expectedAck == AckNone + + + + + Mission request list failed, maximum retries exceeded. + + + + + Retrying %1 REQUEST_LIST retry Count + + + + + Mission read failed, maximum retries exceeded. + + + + + Retrying %1 MISSION_REQUEST retry Count + + + + + Mission write failed, vehicle failed to send final ack. + + + + + Mission write mission count failed, maximum retries exceeded. + + + + + Vehicle did not request all items from ground station: %1 + + + + + Mission remove all, maximum retries exceeded. + + + + + Retrying %1 MISSION_CLEAR_ALL retry Count + + + + + Vehicle did not respond to mission item communication: %1 + + + + + Vehicle requested item outside range, count:request %1:%2. Send to Vehicle failed. + + + + + + + Vehicle returned error: %1. + + + + + Vehicle did not request all items during write sequence, missed count %1. + + + + + Vehicle returned error: %1. Vehicle remove all failed. + + + + + Vehicle returned error: %1. %2Vehicle did not accept guided item. + + + + + Mission accepted (MAV_MISSION_ACCEPTED) + + + + + Unspecified error (MAV_MISSION_ERROR) + + + + + Coordinate frame is not supported (MAV_MISSION_UNSUPPORTED_FRAME) + + + + + Command is not supported (MAV_MISSION_UNSUPPORTED) + + + + + Mission item exceeds storage space (MAV_MISSION_NO_SPACE) + + + + + One of the parameters has an invalid value (MAV_MISSION_INVALID) + + + + + Param1 has an invalid value (MAV_MISSION_INVALID_PARAM1) + + + + + Param2 has an invalid value (MAV_MISSION_INVALID_PARAM2) + + + + + Param3 has an invalid value (MAV_MISSION_INVALID_PARAM3) + + + + + Param4 has an invalid value (MAV_MISSION_INVALID_PARAM4) + + + + + X/Param5 has an invalid value (MAV_MISSION_INVALID_PARAM5_X) + + + + + Y/Param6 has an invalid value (MAV_MISSION_INVALID_PARAM6_Y) + + + + + Param7 has an invalid value (MAV_MISSION_INVALID_PARAM7) + + + + + Received mission item out of sequence (MAV_MISSION_INVALID_SEQUENCE) + + + + + Not accepting any mission commands (MAV_MISSION_DENIED) + + + + + QGC Internal Error + + + PlanMasterController - - Error reading Plan file (%1). %2 + + Download not supported on high latency links. + + + + + Upload not supported on high latency links. + + + + + Error loading Plan file (%1). %2 - + Plan save error %1 : %2 - + + KML save error %1 : %2 + + + + Supported types (*.%1 *.%2 *.%3 *.%4) - - + + + All Files (*.*) - + Plan Files (*.%1) + + + KML Files (*.%1) + + PlanToolBar - + Selected Waypoint - + Alt diff: - + Azimuth: - - + + Distance: - + Gradient: - + Heading: - + Total Mission - + Max telem dist: - + Time: - + Battery - + Batteries required: - - Swap waypoint: - - - - + Upload Required - + Upload @@ -6220,155 +8488,175 @@ Is this really what you want? PlanView - + Vehicle is currently armed. Do you want to upload the mission to the vehicle? - + Apply new alititude - + You have changed the default altitude for mission items. Would you like to apply that altitude to all the items in the current mission? - + Your vehicle is currently flying a mission. In order to upload a new or modified mission the current mission will be paused. - + After the mission is uploaded you can adjust the current waypoint and start the mission. - + Pause and Upload - + + You need at least one item to create a KML. + + + + Plan Upload - + Select Plan File - + Save Plan - + + Save KML + + + + Move the selected mission item to the be after following mission item: - + Plan - + Mission - + Fence - + Rally - + You have unsaved/unsent changes. Loading from the Vehicle will lose these changes. Are you sure you want to load from the Vehicle? - + You have unsaved/unsent changes. Loading from a file will lose these changes. Are you sure you want to load from a file? - + Are you sure you want to remove all items? - + This will also remove all items from the vehicle. - + Create complex pattern: - + Mission overwrite - + GeoFence overwrite - + Rally Points overwrite - + You have unsaved changes. You should upload to your vehicle, or save to a file: - + Sync: - + Upload - + Download - + Save To File... - + Load From File... - + Remove All - + Remove all + + + Save KML... + + + + + KML + + PolygonEditor @@ -6401,6 +8689,11 @@ Is this really what you want? PowerComponent + + + + + @@ -6410,197 +8703,315 @@ Is this really what you want? + %1 cannot perform ESC Calibration with this version of firmware. You will need to upgrade to a newer firmware. + %1 cannot perform ESC Calibration with this version of firmware. You will need to upgrade %1. + Performing calibration. This will take a few seconds.. + + ESC Calibration failed + Calibration complete. You can disconnect your battery now if you like. + WARNING: Props must be removed from vehicle prior to performing ESC calibration. + Connect the battery now and calibration will begin. + You must disconnect the battery prior to performing ESC Calibration. Disconnect your battery and try again. + + + + Measure battery voltage using an external voltmeter and enter the value below. Click Calculate to set the new voltage multiplier. + + + + + + Measured voltage: + + + + + + Vehicle voltage: + + + + + + Voltage divider: + + + + + + Measure current draw using an external current meter and enter the value below. Click Calculate to set the new amps per volt value. + + + + + + Measured current: + + + + + + Vehicle current: + + + + + + Amps per volt: + + + + + + + + + + Calculate + + + Battery + Number of Cells (in Series) + Full Voltage (per cell) + Battery Max: + Empty Voltage (per cell) + Battery Min: + Voltage divider + Calculate Voltage Divider + + + + If the battery voltage reported by the vehicle is largely different than the voltage read externally using a voltmeter you can adjust the voltage multiplier value to correct this. + + + + + + + + Click the Calculate button for help with calculating a new value. + + + Amps per volt + Calculate Amps per Volt + + + If the current draw reported by the vehicle is largely different than the current read externally using a current meter you can adjust the amps per volt value to correct this. + + + + ESC PWM Minimum and Maximum Calibration + WARNING: Propellers must be removed from vehicle prior to performing ESC calibration. + You must use USB connection for this operation. + Calibrate + Show UAVCAN Settings + UAVCAN Bus Configuration + Change required restart + UAVCAN Motor Index and Direction Assignment + WARNING: Propellers must be removed from vehicle prior to performing UAVCAN ESC configuration. + ESC parameters will only be accessible in the editor after assignment. + Start the process, then turn each motor into its turn direction, in the order of their motor indices. + Start Assignment + Stop Assignment + Show Advanced Settings + Advanced Power Settings + Voltage Drop on Full Load (per cell) + Batteries show less voltage at high throttle. Enter the difference in Volts between idle throttle and full + throttle, divided by the number of battery cells. Leave at the default if unsure. + If this value is set too high, the battery might be deep discharged and damaged. + Compensated Minimum Voltage: + V @@ -6619,16 +9030,19 @@ Is this really what you want? PowerComponentSummary + Battery Full: + Battery Empty: + Number of Cells: @@ -6637,77 +9051,117 @@ Is this really what you want? QGCApplication - + You are running %1 as root. You should not do this since it will cause other issues with %1. %1 will now exit. If you are having serial port issues on Ubuntu, execute the following commands to fix most issues: sudo usermod -a -G dialout $USER sudo apt-get remove modemmanager - + Telemetry save error - + Unable to save telemetry log. Error copying telemetry to '%1': '%2'. - + + The format for QGroundControl saved settings has been modified. Your saved settings have been reset to defaults. + + + + + The Offline Map Cache database has been upgraded. Your old map cache sets have been reset. + + + + Telemetry Save Error - + Unable to save telemetry log. Application save directory is not set. - + Unable to save telemetry log. Telemetry save directory "%1" does not exist. + + + Parameters are missing from firmware. You may be running a version of firmware QGC does not work correctly with or your firmware has a bug in it. Missing params: %1 + + QGCCorePlugin - + General - + Comm Links - + Offline Maps - + MAVLink - + Console - + Mock Link - + Debug - + + Values + + + + + Camera + + + + + Video Stream + + + + + Health + + + + + Vibration + + + + WARNING: You are about to enter Advanced Mode. If used incorrectly, this may cause your vehicle to malfunction thus voiding your warranty. You should do so only if instructed by customer support. Are you sure you want to enable Advanced Mode? @@ -6715,31 +9169,60 @@ sudo apt-get remove modemmanager QGCFileDialog - + + + Delete + + + + No files - + New file name: - + File names must end with .%1 file extension. If missing it will be added. - + The file %1 exists. Click Save again to replace it. - + Save to existing file: + + QGCFileDownload + + + Could not save downloaded file to %1. Error: %2 + + + + + Download cancelled + + + + + Error: File Not Found + + + + + Error during download. Error: %1 + + + QGCFlightGearLink @@ -7127,7 +9610,7 @@ sudo apt-get remove modemmanager QGCLogEntry - + Pending @@ -7176,6 +9659,11 @@ sudo apt-get remove modemmanager Type + + + Vehicle %1 + + QGCMAVLinkLogPlayer @@ -7239,6 +9727,123 @@ sudo apt-get remove modemmanager + + QGCMapPolygon + + + File not found: %1 + + + + + Unable to open file: %1 error: $%2 + + + + + Unable to parse KML file: %1 error: %2 line: %3 + + + + + Unable to find Polygon node in KML + + + + + Internal error: Unable to find coordinates node in KML + + + + + QGCMapPolygonVisuals + + + Select KML File + + + + + KML files (*.kml) + + + + + Circle + + + + + Polygon + + + + + Set radius... + + + + + Edit position... + + + + + Edit Position + + + + + Load KML... + + + + + Radius: + + + + + QGCMapPolyline + + + File not found: %1 + + + + + Unable to open file: %1 error: $%2 + + + + + Unable to parse KML file: %1 error: %2 line: %3 + + + + + Unable to find Polygon node in KML + + + + + Internal error: Unable to find coordinates node in KML + + + + + QGCMapPolylineVisuals + + + Select KML File + + + + + KML files (*.kml) + + + QGCMapRCToParamDialog @@ -7364,10 +9969,12 @@ Do you want to replace it? - QGCTabbedInfoView + QGCQuickWidget - - Info View + + Source not ready: Status(%1) +Errors: +%2 @@ -7390,9 +9997,30 @@ Do you want to replace it? + Upload File + + + Download Directory + + + + + Downloading: %1 + + + + + Uploading: %1 + + + + + Error: %1 + + QGCUASFileViewMulti @@ -7413,91 +10041,94 @@ Do you want to replace it? QGCView - + + showDialog called before QGCView.completed signalled + + + + + QGCViewDialogContainer + + Ok - - + + Open - + Save - + Apply - + Save All - + Yes - + Yes to All - + Retry - + Reset - + Restore to Defaults - + Ignore - + Cancel - + Close - + No - + No to All - + Abort - - - showDialog called before QGCView.completed signalled - - QGCWebView @@ -7555,12 +10186,12 @@ Do you want to replace it? - + Receiving from XPlane at %1 Hz - + Receiving from XPlane. @@ -7641,38 +10272,49 @@ Do you want to replace it? - - enum strings/values count mismatch strings:values %1:%2 + + enum strings/values count mismatch in %3 strings:values %1:%2 - + Incorrect file type key expected:%1 actual:%2 - + Incorrect type for version value, must be integer - + File version %1 is no longer supported - + File version %1 is newer than current supported version %2 - + value for coordinate array is not array - - Unknown type: %1 + + Unknown type: %1 + + + + + + Guided mode not supported by Vehicle. + + + + + Follow Me @@ -7927,20 +10569,124 @@ Do you want to replace it? - + Mode 1 - + Mode 2 + + RadioComponentController + + + Lower the Throttle stick all the way down as shown in diagram. + +It is recommended to disconnect all motors for additional safety, however, the system is designed to not arm during the calibration. + +Click Next to continue + + + + + Lower the Throttle stick all the way down as shown in diagram. +Reset all transmitter trims to center. + +Please ensure all motor power is disconnected AND all props are removed from the vehicle. + +Click Next to continue + + + + + Move the Throttle stick all the way up and hold it there... + + + + + Move the Throttle stick all the way down and leave it there... + + + + + Move the Yaw stick all the way to the left and hold it there... + + + + + Move the Yaw stick all the way to the right and hold it there... + + + + + Move the Roll stick all the way to the left and hold it there... + + + + + Move the Roll stick all the way to the right and hold it there... + + + + + Move the Pitch stick all the way down and hold it there... + + + + + Move the Pitch stick all the way up and hold it there... + + + + + Allow the Pitch stick to move back to center... + + + + + Move all the transmitter switches and/or dials back and forth to their extreme positions. + + + + + All settings have been captured. Click Next to write the new parameters to your board. + + + + + Center the Throttle stick as shown in diagram. +Reset all transmitter trims to center. + +Please ensure all motor power is disconnected from the vehicle. + +Click Next to continue + + + + + Next + + + + + Calibrate + + + + + The current calibration settings are now displayed for each channel on screen. + +Click the Next button to upload calibration to board. Click Cancel if you don't want to save these values. + + + RallyPointController - + Rally: %1 @@ -7993,119 +10739,162 @@ Do you want to replace it? SafetyComponent - + + Low Battery Failsafe Trigger - - - + + + + + + Failsafe Action: - + + Battery Warn Level: - + + Battery Failsafe Level: - + + RC Loss Failsafe Trigger - + + RC Loss Timeout: - + + Data Link Loss Failsafe Trigger - + + Data Link Loss Timeout: - + + Geofence Failsafe Trigger - + + Action on breach: - - Max radius: + + + Hardware in the Loop Simulation - - Max altitude: + + + HITL Enabled: + + + + + + Battery Emergency Level: - + + + Max Radius: + + + + + + Max Altitude: + + + + + Return Home Settings - + + Climb to altitude of: - + + Return home, then: - + + Land immediately - + + Loiter and do not land - + + Loiter and land after specified time - + + Loiter Time - + + Loiter Altitude - + + Land Mode Settings - + + Landing Descent Rate: - + + Disarm After: @@ -8123,31 +10912,37 @@ Do you want to replace it? SafetyComponentSummary + RTL min alt: + RTL home alt: + RC loss RTL: + RC loss action: + Link loss action: + Low battery action: @@ -8156,245 +10951,387 @@ Do you want to replace it? SensorsComponent - - - If the orientation is in the direction of flight, select ROTATION_NONE. + + Sensors - - For Compass calibration you will need to rotate your vehicle through a number of positions. Click Ok to start calibration. + + Sensors Setup is used to calibrate the sensors within your vehicle. + + + SensorsComponentController - - For Gyroscope calibration you will need to place your vehicle on a surface and leave it still. Click Ok to start calibration. + + Calibration complete - - For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds. Click Ok to start calibration. + + Calibration failed. Calibration log will be displayed. - - To level the horizon you need to place the vehicle in its level flight position and press OK. + + Unsupported calibration firmware version, using log - - For Airspeed calibration you will need to keep your airspeed sensor out of any wind and then blow across the sensor. + + Place your vehicle into one of the Incomplete orientations shown below and hold it still - - Start the individual calibration steps by clicking one of the buttons to the left. + + Rotate the vehicle continuously as shown in the diagram until marked as Completed - - Set Compass Rotation(s) + + Hold still in the current orientation - - Calibration Cancel + + Place you vehicle into one of the orientations shown below and hold it still - - Waiting for Vehicle to response to Cancel. This may take a few seconds. + + Orientation already completed, place you vehicle into one of the incomplete orientations shown below and hold it still + + + SensorsComponentSummary - - - Autopilot Orientation: + + + Compass 0: - - External Compass Orientation: + + + + + + + Setup required - - External Compass 1 Orientation: + + + + + + + + + + + Ready - - Compass 2 Orientation + + + Compass 1: - - Compass + + + Compass 2: - - Calibrate Compass + + + Gyro: - - Gyroscope + + + Accelerometer: + + + SensorsComponentSummaryFixedWing - - Calibrate Gyro + + + Compass: - - Accelerometer + + + + + + + + + Setup required - - Calibrate Accelerometer + + + + + + + + + Ready - - - Level Horizon + + + Gyro: - - Airspeed + + + Accelerometer: - - Calibrate Airspeed + + + Airspeed: + + + SensorsSetup - - Cancel + + + + + If the orientation is in the direction of flight, select ROTATION_NONE. - - - Set Orientations + + + For Compass calibration you will need to rotate your vehicle through a number of positions. + +Click Ok to start calibration. - - - - - - - Rotate + + + For Gyroscope calibration you will need to place your vehicle on a surface and leave it still. + +Click Ok to start calibration. - - - - - - - Hold Still + + + For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds. + +Click Ok to start calibration. - - Sensors + + + To level the horizon you need to place the vehicle in its level flight position and press OK. - - Sensors Setup is used to calibrate the sensors within your vehicle. + + + For Airspeed calibration you will need to keep your airspeed sensor out of any wind and then blow across the sensor. Do not touch the sensor or obstruct any holes during the calibration. - - - SensorsComponentSummary - - Compass 0: + + + Start the individual calibration steps by clicking one of the buttons to the left. - - - - Setup required + + + Set Compass Rotation(s) - - - - - - Ready + + + Calibration Cancel - - Compass 1: + + + Waiting for Vehicle to response to Cancel. This may take a few seconds. + + + + + + Sensor Calibration + + + + + + Performing sensor calibration over a WiFi connection is known to be unreliable. You should disconnect and perform calibration using a direct USB connection instead. + + + + + + Set autopilot orientation before calibrating. + + + + + + + + Autopilot Orientation: + + + + + + External Compass Orientation: + + + + + + External Compass 1 Orientation: + + + + + + Compass 2 Orientation + + + + + + Compass + + + + + + Calibrate Compass + + + + + + Gyroscope + + + + + + Calibrate Gyro + + + + + + Accelerometer - - Compass 2: + + + Calibrate Accelerometer - - Gyro: + + + + + Level Horizon - - Accelerometer: + + + Airspeed - - - SensorsComponentSummaryFixedWing - - Compass: + + + Calibrate Airspeed - - - - - Setup required + + + Cancel - - - - - Ready + + + Next - - Gyro: + + + + + Set Orientations - - Accelerometer: + + + + + + + + + + + + + Rotate - - Airspeed: + + + + + + + + + + + + + Hold Still @@ -8406,7 +11343,17 @@ Do you want to replace it? - + + Error connecting: Could not create port. %1 + + + + + Error opening port: %1 + + + + Link Error @@ -8482,8 +11429,13 @@ Do you want to replace it? SetupPage - - Setup + + %1 Setup + + + + + (Disabled while the vehicle is armed) @@ -8495,57 +11447,62 @@ Do you want to replace it? - + missing message panel text - + + %1 setup must be completed prior to %2 setup. + + + + %1 does not currently support setup of your vehicle type. - + Vehicle settings and info will display after connecting your vehicle. - + You are currently connected to a vehicle but it did not return the full parameter list. - + As a result, the full set of vehicle setup options are not available. - + Vehicle Setup - + Summary - + Firmware - + PX4Flow - + Joystick - + Parameters @@ -8566,10 +11523,152 @@ Do you want to replace it? SimpleMissionItem - + Unknown: %1 + + + H + + + + + Takeoff + + + + + Land + + + + + VTOL Takeoff + + + + + VTOL Land + + + + + ROI + + + + + StructureScanComplexItem + + + %1 does not support loading this complex mission item type: %2:%3 + + + + + %1 complex item version %2 not supported + + + + + + Structure Scan + + + + + StructureScanEditor + + + Note: Polygon respresents structure surface not vehicle flight path. + + + + + WARNING: Photo interval is below minimum interval (%1 secs) supported by camera. + + + + + Scan Distance + + + + + Layer Height + + + + + Trigger Distance + + + + + Pitch + + + + + Yaw + + + + + Gimbal + + + + + Scan + + + + + Structure height + + + + + # Layers + + + + + Bottom layer alt + + + + + Relative altitude + + + + + Rotate entry point + + + + + Statistics + + + + + Photo count + + + + + Photo interval + + + + + secs + + SurveyItemEditor @@ -8584,143 +11683,155 @@ Do you want to replace it? - + Camera - + Trigger Distance - + + Hover and capture image - + Width - - Height + + WARNING: Photo interval is below minimum interval (%1 secs) supported by camera. + Height + + + + Sensor - + Image - + Focal length - + Front Lap - + Side Lap - + Overlap - - + + + Take images in turnarounds + + + + + Grid - - + + Angle - - + + Turnaround dist - - + + Entry - - + + Refly at 90 degree offset - + Select one: - - + + Altitude - + Ground res - + Spacing - + Relative altitude - + Statistics - + Survey area - + Photo count - + Photo interval - + N/A - + secs @@ -8728,17 +11839,17 @@ Do you want to replace it? SurveyMissionItem - + %1 does not support this version of survey items - + %1 does not support loading this complex mission item type: %2:%3 - + %1 but %2 object is missing @@ -8779,11 +11890,21 @@ Do you want to replace it? TCPLink - - + + Link Error + + + Error on link %1. Connection failed + + + + + Error on link %1. Error on socket: %2. + + TcpSettings @@ -8846,95 +11967,104 @@ Do you want to replace it? + + TransectStyleComplexItem + + + + Corridor Scan + + + UAS - + UNINIT - + Unitialized, booting up. - + BOOT - + Booting system, please wait. - + CALIBRATING - + Calibrating sensors, please wait. - + ACTIVE - + Active, normal operation. - + STANDBY - + Standby mode, ready for launch. - + CRITICAL - + FAILURE: Continuing operation. - + EMERGENCY - + EMERGENCY: Land Immediately! - + SHUTDOWN - + Powering off system. - + UNKNOWN - + Unknown system state @@ -8982,42 +12112,24 @@ Do you want to replace it? - - UASMessageView - - - Form - - - - - UASMessageViewWidget - - - Clear Messages - - - - - UASQuickViewItemSelect - - - Select Item - - - UDPLink - + + UDP Link Error - + Error binding UDP port: %1 + + + Error registering Zeroconf + + UdpSettings @@ -9048,29 +12160,37 @@ Do you want to replace it? - ValuesWidget + VTOLModeIndicator - - Value Widget Setup + + VTOL: Fixed Wing - - Show large compass + + VTOL: Multi-Rotor + + + + + ValuePageWidget + + + Value Widget Setup - + Select the values you want to display: - + Vehicle must be connected to assign values. - + Large @@ -9078,213 +12198,258 @@ Do you want to replace it? Vehicle - + MAVLink Generic - + Fixed Wing - + Multi-Rotor - + VTOL - + Rover - + Sub - + Unknown - + %1 command temporarily rejected - + %1 command denied - + %1 command not supported - + %1 command failed - + AutoLoad%1.%2 - + + %1 low battery: %2 percent remaining + + + + + Mission transfer failed. Retry transfer. Error: %1 + + + + + GeoFence transfer failed. Retry transfer. Error: %1 + + + + + Rally Point transfer failed. Retry transfer. Error: %1 + + + + + %1 communication lost + + + + + %1 communication regained + + + + Generic micro air vehicle - + Fixed wing aircraft - + Quadrotor - + Coaxial helicopter - + Normal helicopter with tail rotor. - + Ground installation - + Operator control unit / ground control station - + Airship, controlled - + Free balloon, uncontrolled - + Rocket - + Ground rover - + Surface vessel, boat, ship - + Submarine - + Hexarotor - - + + Octorotor - - + + Flapping wing - + Onboard companion controller - + Two-rotor VTOL using control surfaces in vertical operation in addition. Tailsitter - + Quad-rotor VTOL using a V-shaped quad config in vertical operation. Tailsitter - + Tiltrotor VTOL - + VTOL reserved 2 - + VTOL reserved 3 - + VTOL reserved 4 - + VTOL reserved 5 - + Onboard gimbal - + Onboard ADSB peripheral - - Vehicle did not respond to command: %1 + + vehicle %1 - - - VehicleHealthWidget - - Vehicle Health + + %1 %2 flight mode - - All systems healthy + + armed + + + + + disarmed + + + + + Vehicle did not respond to command: %1 + + + + + VehicleMapItem + + + Vehicle %1 @@ -9320,43 +12485,75 @@ Do you want to replace it? - VibrationWidget + VibrationPageWidget - + Vibe - + Clip count - + Accel 1: - - + Accel 2: - + + Accel 3: + + + + Not Available + + VideoPageWidget + + + Enable Stream + + + + + Grid Lines + + + + + Stop Recording + + + + + Record Stream + + + + + Video Streaming Not Configured + + + VideoReceiver - + Unabled to record video. Video save path must be specified in Settings. - + Invalid video format defined. @@ -9364,12 +12561,12 @@ Do you want to replace it? ViewWidget - + missing connected implementation - + no vehicle connected diff --git a/qgcresources.qrc b/qgcresources.qrc index e638acf3f5657dfed59c061092206a25c64b76cc..5b6fa54b53d55297f9774605cfbb30482e0a7eb5 100644 --- a/qgcresources.qrc +++ b/qgcresources.qrc @@ -109,6 +109,7 @@ src/FlightMap/Images/compassInstrumentDial.svg src/FlightMap/Images/crossHair.svg src/FlightMap/Images/PiP.svg + src/FlightMap/Images/pipHide.svg src/FlightMap/Images/pipResize.svg src/FlightMap/Images/rollDialWhite.svg src/FlightMap/Images/rollPointerWhite.svg diff --git a/qgroundcontrol.pro b/qgroundcontrol.pro index 418472214a5882a5626d82e41ca749c04ef905f7..fee54686ae51139fd53dfeed5fc56270f9ac6736 100644 --- a/qgroundcontrol.pro +++ b/qgroundcontrol.pro @@ -7,14 +7,16 @@ # License terms set in COPYING.md # ------------------------------------------------- +QMAKE_PROJECT_DEPTH = 0 # undocumented qmake flag to force absolute paths in make files + exists($${OUT_PWD}/qgroundcontrol.pro) { error("You must use shadow build (e.g. mkdir build; cd build; qmake ../qgroundcontrol.pro).") } message(Qt version $$[QT_VERSION]) -!equals(QT_MAJOR_VERSION, 5) | !greaterThan(QT_MINOR_VERSION, 6) { - error("Unsupported Qt version, 5.7+ is required") +!equals(QT_MAJOR_VERSION, 5) | !greaterThan(QT_MINOR_VERSION, 8) { + error("Unsupported Qt version, 5.9+ is required") } include(QGCCommon.pri) @@ -43,8 +45,6 @@ MacBuild { } iOSBuild { - BUNDLE.files = $$files($$PWD/ios/AppIcon*.png) $$PWD/ios/QGCLaunchScreen.xib - QMAKE_BUNDLE_DATA += BUNDLE LIBS += -framework AVFoundation #-- Info.plist (need an "official" one for the App Store) ForAppStore { @@ -63,6 +63,8 @@ iOSBuild { QMAKE_INFO_PLIST = $${BASEDIR}/ios/iOS-Info.plist OTHER_FILES += $${BASEDIR}/ios/iOS-Info.plist } + BUNDLE.files = $$files($$PWD/ios/AppIcon*.png) $$PWD/ios/QGCLaunchScreen.xib $$QMAKE_INFO_PLIST + QMAKE_BUNDLE_DATA += BUNDLE #-- TODO: Add iTunesArtwork } @@ -207,8 +209,7 @@ contains(DEFINES, ENABLE_VERBOSE_OUTPUT) { } else:exists(user_config.pri):infile(user_config.pri, DEFINES, ENABLE_VERBOSE_OUTPUT) { message("Enable verbose compiler output (manual override from user_config.pri)") } else { -CONFIG += \ - silent + CONFIG += silent } QT += \ @@ -233,10 +234,12 @@ QT += \ multimedia } -!MobileBuild { -QT += \ - printsupport \ - serialport \ +AndroidBuild || iOSBuild { + # Android and iOS don't unclude these +} else { + QT += \ + printsupport \ + serialport \ } contains(DEFINES, QGC_ENABLE_BLUETOOTH) { @@ -412,7 +415,9 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/FactSystem/FactSystemTestGeneric.h \ src/FactSystem/FactSystemTestPX4.h \ src/FactSystem/ParameterManagerTest.h \ + src/MissionManager/CameraCalcTest.h \ src/MissionManager/CameraSectionTest.h \ + src/MissionManager/CorridorScanComplexItemTest.h \ src/MissionManager/MissionCommandTreeTest.h \ src/MissionManager/MissionControllerManagerTest.h \ src/MissionManager/MissionControllerTest.h \ @@ -421,10 +426,13 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/MissionManager/MissionSettingsTest.h \ src/MissionManager/PlanMasterControllerTest.h \ src/MissionManager/QGCMapPolygonTest.h \ + src/MissionManager/QGCMapPolylineTest.h \ src/MissionManager/SectionTest.h \ src/MissionManager/SimpleMissionItemTest.h \ src/MissionManager/SpeedSectionTest.h \ + src/MissionManager/StructureScanComplexItemTest.h \ src/MissionManager/SurveyMissionItemTest.h \ + src/MissionManager/TransectStyleComplexItemTest.h \ src/MissionManager/VisualMissionItemTest.h \ src/qgcunittest/FileDialogTest.h \ src/qgcunittest/FileManagerTest.h \ @@ -448,7 +456,9 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/FactSystem/FactSystemTestGeneric.cc \ src/FactSystem/FactSystemTestPX4.cc \ src/FactSystem/ParameterManagerTest.cc \ + src/MissionManager/CameraCalcTest.cc \ src/MissionManager/CameraSectionTest.cc \ + src/MissionManager/CorridorScanComplexItemTest.cc \ src/MissionManager/MissionCommandTreeTest.cc \ src/MissionManager/MissionControllerManagerTest.cc \ src/MissionManager/MissionControllerTest.cc \ @@ -457,10 +467,13 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { src/MissionManager/MissionSettingsTest.cc \ src/MissionManager/PlanMasterControllerTest.cc \ src/MissionManager/QGCMapPolygonTest.cc \ + src/MissionManager/QGCMapPolylineTest.cc \ src/MissionManager/SectionTest.cc \ src/MissionManager/SimpleMissionItemTest.cc \ src/MissionManager/SpeedSectionTest.cc \ + src/MissionManager/StructureScanComplexItemTest.cc \ src/MissionManager/SurveyMissionItemTest.cc \ + src/MissionManager/TransectStyleComplexItemTest.cc \ src/MissionManager/VisualMissionItemTest.cc \ src/qgcunittest/FileDialogTest.cc \ src/qgcunittest/FileManagerTest.cc \ @@ -483,8 +496,9 @@ DebugBuild { PX4FirmwarePlugin { PX4FirmwarePluginFactory { APMFirmwarePlugin { HEADERS += \ src/AnalyzeView/ExifParser.h \ - src/AnalyzeView/ULogParser.h \ + src/AnalyzeView/LogDownloadController.h \ src/AnalyzeView/PX4LogParser.h \ + src/AnalyzeView/ULogParser.h \ src/Audio/AudioOutput.h \ src/Camera/QGCCameraControl.h \ src/Camera/QGCCameraIO.h \ @@ -503,6 +517,7 @@ HEADERS += \ src/MissionManager/CameraSection.h \ src/MissionManager/CameraSpec.h \ src/MissionManager/ComplexMissionItem.h \ + src/MissionManager/CorridorScanComplexItem.h \ src/MissionManager/FixedWingLandingComplexItem.h \ src/MissionManager/GeoFenceController.h \ src/MissionManager/GeoFenceManager.h \ @@ -521,6 +536,7 @@ HEADERS += \ src/MissionManager/QGCFencePolygon.h \ src/MissionManager/QGCMapCircle.h \ src/MissionManager/QGCMapPolygon.h \ + src/MissionManager/QGCMapPolyline.h \ src/MissionManager/RallyPoint.h \ src/MissionManager/RallyPointController.h \ src/MissionManager/RallyPointManager.h \ @@ -529,6 +545,7 @@ HEADERS += \ src/MissionManager/SpeedSection.h \ src/MissionManager/StructureScanComplexItem.h \ src/MissionManager/SurveyMissionItem.h \ + src/MissionManager/TransectStyleComplexItem.h \ src/MissionManager/VisualMissionItem.h \ src/PositionManager/PositionManager.h \ src/PositionManager/SimulatedPosition.h \ @@ -549,6 +566,7 @@ HEADERS += \ src/QGCToolbox.h \ src/QmlControls/AppMessages.h \ src/QmlControls/CoordinateVector.h \ + src/QmlControls/EditPositionDialogController.h \ src/QmlControls/MavlinkQmlSingleton.h \ src/QmlControls/ParameterEditorController.h \ src/QmlControls/QGCFileDialogController.h \ @@ -583,7 +601,7 @@ HEADERS += \ src/uas/UAS.h \ src/uas/UASInterface.h \ src/uas/UASMessageHandler.h \ - src/AnalyzeView/LogDownloadController.h \ + src/UTM.h \ AndroidBuild { HEADERS += \ @@ -676,8 +694,9 @@ AndroidBuild { SOURCES += \ src/AnalyzeView/ExifParser.cc \ - src/AnalyzeView/ULogParser.cc \ + src/AnalyzeView/LogDownloadController.cc \ src/AnalyzeView/PX4LogParser.cc \ + src/AnalyzeView/ULogParser.cc \ src/Audio/AudioOutput.cc \ src/Camera/QGCCameraControl.cc \ src/Camera/QGCCameraIO.cc \ @@ -694,6 +713,7 @@ SOURCES += \ src/MissionManager/CameraSection.cc \ src/MissionManager/CameraSpec.cc \ src/MissionManager/ComplexMissionItem.cc \ + src/MissionManager/CorridorScanComplexItem.cc \ src/MissionManager/FixedWingLandingComplexItem.cc \ src/MissionManager/GeoFenceController.cc \ src/MissionManager/GeoFenceManager.cc \ @@ -712,6 +732,7 @@ SOURCES += \ src/MissionManager/QGCFencePolygon.cc \ src/MissionManager/QGCMapCircle.cc \ src/MissionManager/QGCMapPolygon.cc \ + src/MissionManager/QGCMapPolyline.cc \ src/MissionManager/RallyPoint.cc \ src/MissionManager/RallyPointController.cc \ src/MissionManager/RallyPointManager.cc \ @@ -719,6 +740,7 @@ SOURCES += \ src/MissionManager/SpeedSection.cc \ src/MissionManager/StructureScanComplexItem.cc \ src/MissionManager/SurveyMissionItem.cc \ + src/MissionManager/TransectStyleComplexItem.cc \ src/MissionManager/VisualMissionItem.cc \ src/PositionManager/PositionManager.cpp \ src/PositionManager/SimulatedPosition.cc \ @@ -738,6 +760,7 @@ SOURCES += \ src/QGCToolbox.cc \ src/QmlControls/AppMessages.cc \ src/QmlControls/CoordinateVector.cc \ + src/QmlControls/EditPositionDialogController.cc \ src/QmlControls/ParameterEditorController.cc \ src/QmlControls/QGCFileDialogController.cc \ src/QmlControls/QGCImageProvider.cc \ @@ -770,7 +793,7 @@ SOURCES += \ src/main.cc \ src/uas/UAS.cc \ src/uas/UASMessageHandler.cc \ - src/AnalyzeView/LogDownloadController.cc \ + src/UTM.cpp \ DebugBuild { SOURCES += \ diff --git a/qgroundcontrol.qrc b/qgroundcontrol.qrc index 5a743baf9caed70fc9b10fd98611dd23a4dd7f83..ce7371c7502275d9f819ae8f4b60ad40c684843e 100644 --- a/qgroundcontrol.qrc +++ b/qgroundcontrol.qrc @@ -9,25 +9,35 @@ src/ui/toolbar/GPSRTKIndicator.qml src/ui/toolbar/MessageIndicator.qml src/ui/toolbar/ModeIndicator.qml + src/ui/toolbar/VTOLModeIndicator.qml src/ui/toolbar/RCRSSIIndicator.qml src/ui/toolbar/TelemetryRSSIIndicator.qml src/ui/toolbar/JoystickIndicator.qml + src/PlanView/CorridorScanEditor.qml + src/PlanView/CameraCalc.qml + src/PlanView/CorridorScanMapVisual.qml + src/QmlControls/EditPositionDialog.qml + src/QmlControls/FileButton.qml + src/MissionManager/QGCMapCircleVisuals.qml + src/MissionManager/QGCMapPolylineVisuals.qml + src/PlanView/StructureScanMapVisual.qml src/AnalyzeView/AnalyzeView.qml src/ui/AppSettings.qml src/ui/preferences/BluetoothSettings.qml + src/FlightMap/Widgets/CameraPageWidget.qml src/ViewWidgets/CustomCommandWidget.qml src/ui/preferences/DebugWindow.qml src/AutoPilotPlugins/Common/ESP8266Component.qml src/AutoPilotPlugins/Common/ESP8266ComponentSummary.qml - src/AutoPilotPlugins/Common/SyslinkComponent.qml src/VehicleSetup/FirmwareUpgrade.qml src/FlightDisplay/FlightDisplayViewDummy.qml src/FlightDisplay/FlightDisplayViewUVC.qml + src/PlanView/FWLandingPatternEditor.qml src/ui/preferences/GeneralSettings.qml src/AnalyzeView/GeoTagPage.qml - src/AnalyzeView/MavlinkConsolePage.qml + src/FlightMap/Widgets/HealthPageWidget.qml src/VehicleSetup/JoystickConfig.qml src/ui/preferences/LinkSettings.qml src/AnalyzeView/LogDownloadPage.qml @@ -35,22 +45,26 @@ src/ui/MainWindowHybrid.qml src/ui/MainWindowInner.qml src/ui/MainWindowNative.qml + src/AnalyzeView/MavlinkConsolePage.qml src/ui/preferences/MavlinkSettings.qml - src/PlanView/PlanView.qml + src/PlanView/MissionSettingsEditor.qml src/ui/preferences/MockLink.qml src/ui/preferences/MockLinkSettings.qml src/AutoPilotPlugins/Common/MotorComponent.qml src/QtLocationPlugin/QMLControl/OfflineMap.qml + src/PlanView/PlanView.qml src/VehicleSetup/PX4FlowSensor.qml + src/FlightMap/Widgets/QGCInstrumentWidget.qml + src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml src/AnalyzeView/AnalyzePage.qml src/QmlControls/AppMessages.qml - src/PlanView/CameraCalc.qml src/PlanView/CameraSection.qml src/QmlControls/ClickableColor.qml + src/QmlControls/DeadMouseArea.qml src/QmlControls/DropButton.qml + src/QmlControls/DropPanel.qml src/QmlControls/ExclusiveGroupItem.qml src/QmlControls/FactSliderPanel.qml - src/QmlControls/FileButton.qml src/QmlControls/FlightModeDropdown.qml src/QmlControls/FlightModeMenu.qml src/PlanView/FWLandingPatternMapVisual.qml @@ -67,7 +81,6 @@ src/PlanView/MissionItemStatus.qml src/QmlControls/ModeSwitchDisplay.qml src/QmlControls/MultiRotorMotorDisplay.qml - src/QmlControls/NoMouseThroughRectangle.qml src/QmlControls/OfflineMapButton.qml src/QmlControls/PageView.qml src/QmlControls/ParameterEditor.qml @@ -85,17 +98,18 @@ src/QmlControls/QGCLabel.qml src/QmlControls/QGCListView.qml src/QmlControls/QGCMapLabel.qml - src/MissionManager/QGCMapCircleVisuals.qml src/MissionManager/QGCMapPolygonVisuals.qml src/QmlControls/QGCMouseArea.qml src/QmlControls/QGCMovableItem.qml src/QmlControls/QGCPipable.qml src/QmlControls/QGCRadioButton.qml src/QmlControls/QGCSlider.qml + src/QmlControls/QGCSwitch.qml src/QmlControls/QGCTextField.qml src/QmlControls/QGCToolBarButton.qml src/QmlControls/QGCView.qml src/QmlControls/QGCViewDialog.qml + src/QmlControls/QGCViewDialogContainer.qml src/QmlControls/QGCViewMessage.qml src/QmlControls/QGCViewPanel.qml src/QmlControls/QGroundControl.Controls.qmldir @@ -109,13 +123,11 @@ src/ui/toolbar/SignalStrength.qml src/PlanView/SimpleItemMapVisual.qml src/QmlControls/SliderSwitch.qml - src/PlanView/StructureScanMapVisual.qml src/QmlControls/SubMenuButton.qml src/PlanView/SurveyMapVisual.qml + src/QmlControls/ToolStrip.qml src/QmlControls/VehicleRotationCal.qml src/QmlControls/VehicleSummaryRow.qml - src/QmlControls/ToolStrip.qml - src/QmlControls/DropPanel.qml src/ViewWidgets/ViewWidget.qml src/FactSystem/FactControls/FactBitmask.qml src/FactSystem/FactControls/FactCheckBox.qml @@ -130,9 +142,9 @@ src/FlightDisplay/FlightDisplayViewMap.qml src/FlightDisplay/FlightDisplayViewVideo.qml src/FlightDisplay/FlightDisplayViewWidgets.qml - src/FlightDisplay/GuidedActionsController.qml src/FlightDisplay/GuidedActionConfirm.qml src/FlightDisplay/GuidedActionList.qml + src/FlightDisplay/GuidedActionsController.qml src/FlightDisplay/GuidedAltitudeSlider.qml src/FlightDisplay/MultiVehicleList.qml src/FlightDisplay/qmldir @@ -141,9 +153,9 @@ src/FlightMap/Widgets/CenterMapDropPanel.qml src/FlightMap/Widgets/CompassRing.qml src/FlightMap/MapItems/CustomMapItems.qml - src/FlightMap/Widgets/MapFitFunctions.qml src/FlightMap/FlightMap.qml src/FlightMap/Widgets/InstrumentSwipeView.qml + src/FlightMap/Widgets/MapFitFunctions.qml src/FlightMap/MapScale.qml src/FlightMap/MapItems/MissionItemIndicator.qml src/FlightMap/MapItems/MissionItemIndicatorDrag.qml @@ -155,8 +167,6 @@ src/FlightMap/Widgets/QGCAttitudeHUD.qml src/FlightMap/Widgets/QGCAttitudeWidget.qml src/FlightMap/Widgets/QGCCompassWidget.qml - src/FlightMap/Widgets/QGCInstrumentWidget.qml - src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml src/FlightMap/Widgets/QGCPitchIndicator.qml src/FlightMap/QGCVideoBackground.qml src/FlightMap/qmldir @@ -171,52 +181,54 @@ src/PlanView/SimpleItemEditor.qml src/PlanView/StructureScanEditor.qml src/PlanView/SurveyItemEditor.qml - src/PlanView/FWLandingPatternEditor.qml - src/PlanView/MissionSettingsEditor.qml + src/AutoPilotPlugins/Common/SyslinkComponent.qml src/ui/preferences/TcpSettings.qml src/test.qml src/ui/preferences/UdpSettings.qml - src/VehicleSetup/VehicleSummary.qml - src/FlightDisplay/VirtualJoystick.qml - src/FlightMap/Widgets/CameraPageWidget.qml src/FlightMap/Widgets/ValuePageWidget.qml - src/FlightMap/Widgets/HealthPageWidget.qml + src/VehicleSetup/VehicleSummary.qml src/FlightMap/Widgets/VibrationPageWidget.qml + src/FlightMap/Widgets/VideoPageWidget.qml + src/FlightDisplay/VirtualJoystick.qml + src/MissionManager/CameraCalc.FactMetaData.json + src/MissionManager/CameraSpec.FactMetaData.json + src/MissionManager/CorridorScan.SettingsGroup.json + src/QmlControls/EditPositionDialog.FactMetaData.json + src/MissionManager/QGCMapCircle.Facts.json + src/MissionManager/StructureScan.SettingsGroup.json + src/MissionManager/TransectStyle.SettingsGroup.json + src/Settings/App.SettingsGroup.json + src/Settings/AutoConnect.SettingsGroup.json + src/Settings/BrandImage.SettingsGroup.json + src/MissionManager/CameraSection.FactMetaData.json + src/Settings/FlightMap.SettingsGroup.json + src/MissionManager/FWLandingPattern.FactMetaData.json + src/Settings/Guided.SettingsGroup.json src/MissionManager/MavCmdInfoCommon.json src/MissionManager/MavCmdInfoFixedWing.json src/MissionManager/MavCmdInfoMultiRotor.json src/MissionManager/MavCmdInfoRover.json src/MissionManager/MavCmdInfoSub.json src/MissionManager/MavCmdInfoVTOL.json - src/Settings/App.SettingsGroup.json - src/Settings/AutoConnect.SettingsGroup.json - src/Settings/FlightMap.SettingsGroup.json - src/Settings/Guided.SettingsGroup.json - src/MissionManager/QGCMapCircle.Facts.json + src/MissionManager/MissionSettings.FactMetaData.json + src/MissionManager/RallyPoint.FactMetaData.json src/Settings/RTK.SettingsGroup.json + src/MissionManager/SpeedSection.FactMetaData.json src/MissionManager/Survey.SettingsGroup.json - src/MissionManager/StructureScan.SettingsGroup.json src/Settings/Units.SettingsGroup.json - src/Settings/Video.SettingsGroup.json - src/MissionManager/RallyPoint.FactMetaData.json - src/MissionManager/FWLandingPattern.FactMetaData.json src/comm/USBBoardInfo.json - src/MissionManager/CameraSection.FactMetaData.json - src/MissionManager/CameraCalc.FactMetaData.json - src/MissionManager/CameraSpec.FactMetaData.json - src/MissionManager/SpeedSection.FactMetaData.json - src/MissionManager/MissionSettings.FactMetaData.json - src/Vehicle/VehicleFact.json src/Vehicle/BatteryFact.json + src/Vehicle/ClockFact.json src/Vehicle/GPSFact.json src/Vehicle/GPSRTKFact.json - src/Vehicle/WindFact.json - src/Vehicle/VibrationFact.json - src/Vehicle/TemperatureFact.json src/Vehicle/SubmarineFact.json - src/Settings/BrandImage.SettingsGroup.json + src/Vehicle/TemperatureFact.json + src/Vehicle/VehicleFact.json + src/Vehicle/VibrationFact.json + src/Vehicle/WindFact.json + src/Settings/Video.SettingsGroup.json src/comm/APMArduCopterMockLink.params diff --git a/src/AnalyzeView/GeoTagPage.qml b/src/AnalyzeView/GeoTagPage.qml index dbaa678792cba5f29d3c9a91de898843a5e344bd..d4f4386f074ddfd71c5a2526bf3c3349e73b7bff 100644 --- a/src/AnalyzeView/GeoTagPage.qml +++ b/src/AnalyzeView/GeoTagPage.qml @@ -110,7 +110,7 @@ AnalyzePage { } QGCLabel { - text: geoController.saveDirectory != "" ? geoController.saveDirectory : "/TAGGED folder in your image folder" + text: geoController.saveDirectory !== "" ? geoController.saveDirectory : "/TAGGED folder in your image folder" anchors.verticalCenter: parent.verticalCenter } } diff --git a/src/AnalyzeView/LogDownloadController.cc b/src/AnalyzeView/LogDownloadController.cc index 57fab3975512ca112688a79fcad5b73ad112f7b5..133a55671663451192a5d4ad5481485970413585 100644 --- a/src/AnalyzeView/LogDownloadController.cc +++ b/src/AnalyzeView/LogDownloadController.cc @@ -21,6 +21,7 @@ #include "QGCMapEngine.h" #include "ParameterManager.h" #include "Vehicle.h" +#include "SettingsManager.h" #include #include @@ -511,7 +512,7 @@ LogDownloadController::download(QString path) QString dir = path; #if defined(__mobile__) if(dir.isEmpty()) { - dir = QDir::homePath(); + dir = qgcApp()->toolbox()->settingsManager()->appSettings()->logSavePath(); } #else if(dir.isEmpty()) { diff --git a/src/AnalyzeView/LogDownloadTest.cc b/src/AnalyzeView/LogDownloadTest.cc index 364fa740f14be774c9c4a8c89fec9000b1fd321c..4d2c2b7807f12a7c902e73ef32b267b8b3cdfc8c 100644 --- a/src/AnalyzeView/LogDownloadTest.cc +++ b/src/AnalyzeView/LogDownloadTest.cc @@ -57,7 +57,7 @@ void LogDownloadTest::downloadTest(void) } _multiSpyLogDownloadController->clearAllSignals(); - QString downloadFile = QDir(downloadTo).filePath("log_0_UnknownDate.px4log"); + QString downloadFile = QDir(downloadTo).filePath("log_0_UnknownDate.ulg"); QVERIFY(UnitTest::fileCompare(downloadFile, _mockLink->logDownloadFile())); QFile::remove(downloadFile); diff --git a/src/AutoPilotPlugins/APM/APMLightsComponent.qml b/src/AutoPilotPlugins/APM/APMLightsComponent.qml index 8e16b04e4b9c18bb642b5ee3fed6fdcdd5daa6c0..d12a51fd416a8e4170efb30af114c1c0a949a449 100644 --- a/src/AutoPilotPlugins/APM/APMLightsComponent.qml +++ b/src/AutoPilotPlugins/APM/APMLightsComponent.qml @@ -11,6 +11,7 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 +import QGroundControl 1.0 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 @@ -32,6 +33,8 @@ SetupPage { QGCPalette { id: palette; colorGroupEnabled: true } + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _oldFW: !(_activeVehicle.firmwareMajorVersion > 3 || _activeVehicle.firmwareMinorVersion > 5 || _activeVehicle.firmwarePatchVersion >= 2) property Fact _rc5Function: controller.getParameterFact(-1, "r.SERVO5_FUNCTION") property Fact _rc6Function: controller.getParameterFact(-1, "r.SERVO6_FUNCTION") property Fact _rc7Function: controller.getParameterFact(-1, "r.SERVO7_FUNCTION") @@ -42,7 +45,8 @@ SetupPage { property Fact _rc12Function: controller.getParameterFact(-1, "r.SERVO12_FUNCTION") property Fact _rc13Function: controller.getParameterFact(-1, "r.SERVO13_FUNCTION") property Fact _rc14Function: controller.getParameterFact(-1, "r.SERVO14_FUNCTION") - property Fact _stepSize: controller.getParameterFact(-1, "JS_LIGHTS_STEP") + property Fact _stepSize: _oldFW ? controller.getParameterFact(-1, "JS_LIGHTS_STEP") : null // v3.5.1 and prior + property Fact _numSteps: _oldFW ? null : controller.getParameterFact(-1, "JS_LIGHTS_STEPS") // v3.5.2 and up readonly property real _margins: ScreenTools.defaultFontPixelHeight readonly property int _rcFunctionDisabled: 0 @@ -88,22 +92,30 @@ SetupPage { } function calcCurrentStep() { - var i = 1 - for(i; i <= 10; i++) { - var stepSize = (1900-1100)/i - if(_stepSize.value >= stepSize) { - _stepSize.value = stepSize; - break; + if (_oldFW) { + var i = 1 + for(i; i <= 10; i++) { + var stepSize = (1900-1100)/i + if(_stepSize.value >= stepSize) { + _stepSize.value = stepSize; + break; + } } + if (_stepSize.value < 80) { + _stepSize.value = 80; + } + lightsLoader.lightsSteps = i + } else { + lightsLoader.lightsSteps = _numSteps.value } - if (_stepSize.value < 80) { - _stepSize.value = 80; - } - lightsLoader.lightsSteps = i } function calcStepSize(steps) { - _stepSize.value = (1900-1100)/steps + if (_oldFW) { + _stepSize.value = (1900-1100)/steps + } else { + _numSteps.value = steps + } } // Whenever any SERVO#_FUNCTION parameters chagnes we need to go looking for light output channels again diff --git a/src/AutoPilotPlugins/APM/APMPowerComponent.qml b/src/AutoPilotPlugins/APM/APMPowerComponent.qml index e4c6ac0d28d4528d874be0cc54347a275cb630d8..16e036fff74015f2320427fcdc9ea27a7c648913 100644 --- a/src/AutoPilotPlugins/APM/APMPowerComponent.qml +++ b/src/AutoPilotPlugins/APM/APMPowerComponent.qml @@ -29,6 +29,7 @@ SetupPage { Column { spacing: _margins + property Fact armVoltMin: controller.getParameterFact(-1, "r.ARMING_VOLT_MIN") property Fact battAmpPerVolt: controller.getParameterFact(-1, "BATT_AMP_PERVOLT") property Fact battCapacity: controller.getParameterFact(-1, "BATT_CAPACITY") property Fact battCurrPin: controller.getParameterFact(-1, "BATT_CURR_PIN") @@ -239,6 +240,18 @@ SetupPage { QGCLabel { Layout.row: 2 Layout.column: 0 + text: qsTr("Minimum arming voltage:") + } + + FactTextField { + id: armVoltField + width: _fieldWidth + fact: armVoltMin + } + + QGCLabel { + Layout.row: 3 + Layout.column: 0 text: qsTr("Power sensor:") } @@ -260,7 +273,7 @@ SetupPage { } QGCLabel { - Layout.row: 3 + Layout.row: 4 Layout.column: 0 text: qsTr("Current pin:") visible: _showAdvanced @@ -274,7 +287,7 @@ SetupPage { } QGCLabel { - Layout.row: 4 + Layout.row: 5 Layout.column: 0 text: qsTr("Voltage pin:") visible: _showAdvanced @@ -288,7 +301,7 @@ SetupPage { } QGCLabel { - Layout.row: 5 + Layout.row: 6 Layout.column: 0 text: qsTr("Voltage multiplier:") visible: _showAdvanced diff --git a/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml b/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml index 752951d0df72ce63401ac637baf06266b364e72e..4b3d2328fcccc0a9bbc37b64851fd0a0a33615b6 100644 --- a/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml +++ b/src/AutoPilotPlugins/APM/APMSafetyComponentSub.qml @@ -22,7 +22,6 @@ import QGroundControl.ScreenTools 1.0 SetupPage { id: safetyPage pageComponent: safetyPageComponent - visibleWhileArmed: true Component { id: safetyPageComponent diff --git a/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml b/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml index 807f8632ce98dc607ae38daa4cbb568280dacdbb..c32cf6fe4956cecbdff71a7474fb201bf151ff05 100644 --- a/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml +++ b/src/AutoPilotPlugins/APM/APMSubFrameComponent.qml @@ -12,6 +12,7 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Dialogs 1.2 +import QGroundControl 1.0 import QGroundControl.FactSystem 1.0 import QGroundControl.FactControls 1.0 import QGroundControl.Palette 1.0 @@ -22,12 +23,9 @@ import QGroundControl.Controllers 1.0 SetupPage { id: subFramePage pageComponent: subFramePageComponent - property var _flatParamList: ListModel { - ListElement { - name: "Blue Robotics BlueROV2" - file: "Sub/bluerov2-3_5.params" - } - } + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _oldFW: !(_activeVehicle.firmwareMajorVersion > 3 || _activeVehicle.firmwareMinorVersion > 5 || _activeVehicle.firmwarePatchVersion >= 2) APMAirframeComponentController { id: controller; factPanel: subFramePage.viewPanel } @@ -194,18 +192,16 @@ SetupPage { spacing : _margins layoutDirection: Qt.Vertical; - Repeater { - id: airframePicker - model: _flatParamList - - delegate: QGCButton { - width: parent.width - text: name - - onClicked : { - controller.loadParameters(file) - hideDialog() - } + QGCButton { + width: parent.width + text: "Blue Robotics BlueROV2" + property var file: _oldFW ? "Sub/bluerov2-3_5.params" : "Sub/bluerov2-3_5_2.params" + + onClicked : { + console.log(_oldFW) + console.log(_activeVehicle.firmwarePatchVersion) + controller.loadParameters(file) + hideDialog() } } } diff --git a/src/AutoPilotPlugins/AutoPilotPlugin.cc b/src/AutoPilotPlugins/AutoPilotPlugin.cc index 731e43dd9b4998beb3ec996ae930279d755690d6..6107fd4551bded99871d1979c2ce16527213e38c 100644 --- a/src/AutoPilotPlugins/AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/AutoPilotPlugin.cc @@ -61,6 +61,17 @@ bool AutoPilotPlugin::setupComplete(void) void AutoPilotPlugin::parametersReadyPreChecks(void) { _recalcSetupComplete(); + + // Connect signals in order to keep setupComplete up to date + foreach(const QVariant componentVariant, vehicleComponents()) { + VehicleComponent* component = qobject_cast(qvariant_cast(componentVariant)); + if (component) { + connect(component, &VehicleComponent::setupCompleteChanged, this, &AutoPilotPlugin::_recalcSetupComplete); + } else { + qWarning() << "AutoPilotPlugin::_recalcSetupComplete Incorrectly typed VehicleComponent"; + } + } + if (!_setupComplete) { qgcApp()->showMessage(tr("One or more vehicle components require setup prior to flight.")); diff --git a/src/AutoPilotPlugins/AutoPilotPlugin.h b/src/AutoPilotPlugins/AutoPilotPlugin.h index e612030d697ed89b8d78f357a38d57983280d585..66728f885bfd6b0e0030ef9277828149bf8c11d1 100644 --- a/src/AutoPilotPlugins/AutoPilotPlugin.h +++ b/src/AutoPilotPlugins/AutoPilotPlugin.h @@ -66,7 +66,7 @@ protected: FirmwarePlugin* _firmwarePlugin; bool _setupComplete; -private: +private slots: void _recalcSetupComplete(void); }; diff --git a/src/AutoPilotPlugins/Common/MotorComponent.qml b/src/AutoPilotPlugins/Common/MotorComponent.qml index dc36a82987cdf1a0d009b3c8c518a15771887561..a964c547f88d72650d379157d5c8c69c0733211c 100644 --- a/src/AutoPilotPlugins/Common/MotorComponent.qml +++ b/src/AutoPilotPlugins/Common/MotorComponent.qml @@ -55,7 +55,7 @@ SetupPage { property real _lastValue: 0 onTriggered: { - if (_lastValue != slider.value) { + if (_lastValue !== slider.value) { controller.vehicle.motorTest(index + 1, slider.value, 1) } } diff --git a/src/AutoPilotPlugins/Common/RadioComponent.qml b/src/AutoPilotPlugins/Common/RadioComponent.qml index c5fb6c91006c4c7578341727650f582d3a562a0b..c4f6b57ea7c731da98df445e891536b9dc77cb96 100644 --- a/src/AutoPilotPlugins/Common/RadioComponent.qml +++ b/src/AutoPilotPlugins/Common/RadioComponent.qml @@ -379,7 +379,7 @@ SetupPage { text: qsTr("Calibrate") onClicked: { - if (text == qsTr("Calibrate")) { + if (text === qsTr("Calibrate")) { showDialog(zeroTrimsDialogComponent, dialogTitle, radioPage.showDialogDefaultWidth, StandardButton.Ok | StandardButton.Cancel) } else { controller.nextButtonClicked() diff --git a/src/AutoPilotPlugins/Common/RadioComponentController.cc b/src/AutoPilotPlugins/Common/RadioComponentController.cc index f14f07694d5a5e3336ff8c922ff391056ae63887..2884fec938d1ead79aaffa121b1fe9a6d829b82e 100644 --- a/src/AutoPilotPlugins/Common/RadioComponentController.cc +++ b/src/AutoPilotPlugins/Common/RadioComponentController.cc @@ -123,23 +123,23 @@ RadioComponentController::~RadioComponentController() /// @brief Returns the state machine entry for the specified state. const RadioComponentController::stateMachineEntry* RadioComponentController::_getStateMachineEntry(int step) const { - static const char* msgBeginPX4 = "Lower the Throttle stick all the way down as shown in diagram.\n\n" + static const char* msgBeginPX4 = QT_TR_NOOP("Lower the Throttle stick all the way down as shown in diagram.\n\n" "It is recommended to disconnect all motors for additional safety, however, the system is designed to not arm during the calibration.\n\n" - "Click Next to continue"; - static const char* msgBeginAPM = "Lower the Throttle stick all the way down as shown in diagram.\nReset all transmitter trims to center.\n\n" + "Click Next to continue"); + static const char* msgBeginAPM = QT_TR_NOOP("Lower the Throttle stick all the way down as shown in diagram.\nReset all transmitter trims to center.\n\n" "Please ensure all motor power is disconnected AND all props are removed from the vehicle.\n\n" - "Click Next to continue"; - static const char* msgThrottleUp = "Move the Throttle stick all the way up and hold it there..."; - static const char* msgThrottleDown = "Move the Throttle stick all the way down and leave it there..."; - static const char* msgYawLeft = "Move the Yaw stick all the way to the left and hold it there..."; - static const char* msgYawRight = "Move the Yaw stick all the way to the right and hold it there..."; - static const char* msgRollLeft = "Move the Roll stick all the way to the left and hold it there..."; - static const char* msgRollRight = "Move the Roll stick all the way to the right and hold it there..."; - static const char* msgPitchDown = "Move the Pitch stick all the way down and hold it there..."; - static const char* msgPitchUp = "Move the Pitch stick all the way up and hold it there..."; - static const char* msgPitchCenter = "Allow the Pitch stick to move back to center..."; - static const char* msgSwitchMinMax = "Move all the transmitter switches and/or dials back and forth to their extreme positions."; - static const char* msgComplete = "All settings have been captured. Click Next to write the new parameters to your board."; + "Click Next to continue"); + static const char* msgThrottleUp = QT_TR_NOOP("Move the Throttle stick all the way up and hold it there..."); + static const char* msgThrottleDown = QT_TR_NOOP("Move the Throttle stick all the way down and leave it there..."); + static const char* msgYawLeft = QT_TR_NOOP("Move the Yaw stick all the way to the left and hold it there..."); + static const char* msgYawRight = QT_TR_NOOP("Move the Yaw stick all the way to the right and hold it there..."); + static const char* msgRollLeft = QT_TR_NOOP("Move the Roll stick all the way to the left and hold it there..."); + static const char* msgRollRight = QT_TR_NOOP("Move the Roll stick all the way to the right and hold it there..."); + static const char* msgPitchDown = QT_TR_NOOP("Move the Pitch stick all the way down and hold it there..."); + static const char* msgPitchUp = QT_TR_NOOP("Move the Pitch stick all the way up and hold it there..."); + static const char* msgPitchCenter = QT_TR_NOOP("Allow the Pitch stick to move back to center..."); + static const char* msgSwitchMinMax = QT_TR_NOOP("Move all the transmitter switches and/or dials back and forth to their extreme positions."); + static const char* msgComplete = QT_TR_NOOP("All settings have been captured. Click Next to write the new parameters to your board."); static const stateMachineEntry rgStateMachinePX4[] = { //Function @@ -206,9 +206,9 @@ void RadioComponentController::_advanceState(void) /// @brief Sets up the state machine according to the current step from _currentStep. void RadioComponentController::_setupCurrentState(void) { - static const char* msgBeginAPMRover = "Center the Throttle stick as shown in diagram.\nReset all transmitter trims to center.\n\n" + static const char* msgBeginAPMRover = QT_TR_NOOP("Center the Throttle stick as shown in diagram.\nReset all transmitter trims to center.\n\n" "Please ensure all motor power is disconnected from the vehicle.\n\n" - "Click Next to continue"; + "Click Next to continue"); const stateMachineEntry* state = _getStateMachineEntry(_currentStep); const char* instructions = state->instructions; diff --git a/src/AutoPilotPlugins/Common/SetupPage.qml b/src/AutoPilotPlugins/Common/SetupPage.qml index a13b4a0c515871f66761716175cf7d6a9443062a..a2912e368a4de630873568b4028d8779386e2427 100644 --- a/src/AutoPilotPlugins/Common/SetupPage.qml +++ b/src/AutoPilotPlugins/Common/SetupPage.qml @@ -21,8 +21,9 @@ import QGroundControl.Controllers 1.0 /// Base view control for all Setup pages QGCView { - id: setupView - viewPanel: setupPanel + id: setupView + viewPanel: setupPanel + enabled: !_shouldDisableWhenArmed property alias pageComponent: pageLoader.sourceComponent property string pageName: vehicleComponent ? vehicleComponent.name : "" @@ -30,53 +31,15 @@ QGCView { property real availableWidth: width - pageLoader.x property real availableHeight: height - pageLoader.y - property real _margins: ScreenTools.defaultFontPixelHeight / 2 + property bool _vehicleArmed: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.armed : false + property bool _shouldDisableWhenArmed: _vehicleArmed ? (vehicleComponent ? !vehicleComponent.allowSetupWhileArmed : false) : false - property bool visibleWhileArmed: false + property real _margins: ScreenTools.defaultFontPixelHeight * 0.5 + property string _pageTitle: qsTr("%1 Setup").arg(pageName) - property bool vehicleArmed: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.armed : false - - onVehicleArmedChanged: { - if (visibleWhileArmed) { - return - } - - if (vehicleArmed) { - disabledWhileArmed.visible = true - setupView.viewPanel.enabled = false - } else { - disabledWhileArmed.visible = false - setupView.viewPanel.enabled = true - } - } QGCPalette { id: qgcPal; colorGroupEnabled: setupPanel.enabled } - // Overlay to display when vehicle is armed and the setup page needs - // to be disabled - Item { - id: disabledWhileArmed - visible: false - z: 9999 - anchors.fill: parent - Rectangle { - anchors.fill: parent - color: "black" - opacity: 0.5 - } - - QGCLabel { - anchors.margins: defaultTextWidth * 2 - anchors.fill: parent - verticalAlignment: Text.AlignVCenter - horizontalAlignment: Text.AlignHCenter - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.largeFontPointSize - color: "red" - text: "Setup disabled while the vehicle is armed" - } - } - QGCViewPanel { id: setupPanel anchors.fill: parent @@ -88,13 +51,13 @@ QGCView { clip: true Column { - id: headingColumn - width: setupPanel.width - spacing: _margins + id: headingColumn + width: setupPanel.width + spacing: _margins QGCLabel { font.pointSize: ScreenTools.largeFontPointSize - text: pageName + " " + qsTr("Setup") + text: _shouldDisableWhenArmed ? _pageTitle + "" + qsTr(" (Disabled while the vehicle is armed)") + "" : _pageTitle visible: !ScreenTools.isShortScreen } @@ -112,6 +75,14 @@ QGCView { anchors.topMargin: _margins anchors.top: headingColumn.bottom } + // Overlay to display when vehicle is armed and this setup page needs + // to be disabled + Rectangle { + visible: _shouldDisableWhenArmed + anchors.fill: pageLoader + color: "black" + opacity: 0.5 + } } } } diff --git a/src/AutoPilotPlugins/PX4/AirframeComponent.qml b/src/AutoPilotPlugins/PX4/AirframeComponent.qml index a0920cf22a47e2f9abb70edfea5a7f74a873f182..3d14454af217afd28efdbb22ba2229cbd4005ec9 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponent.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponent.qml @@ -202,12 +202,12 @@ Your vehicle will also be restarted in order to complete the process.") QGCCheckBox { // Although this item is invisible we still use it to manage state id: airframeCheckBox - checked: modelData.name == controller.currentAirframeType + checked: modelData.name === controller.currentAirframeType exclusiveGroup: airframeTypeExclusive visible: false onCheckedChanged: { - if (checked && combo.currentIndex != -1) { + if (checked && combo.currentIndex !== -1) { console.log("check box change", combo.currentIndex) controller.autostartId = modelData.airframes[combo.currentIndex].autostartId } diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc index 122a39ff7df706c2bd8643a2b21e30f8ca965420..c5bc7ef2d41035934a8d527395da438c9501dbd3 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentController.cc +++ b/src/AutoPilotPlugins/PX4/AirframeComponentController.cc @@ -125,8 +125,8 @@ void AirframeComponentController::_rebootAfterStackUnwind(void) QGC::SLEEP::usleep(500); qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); } - qgcApp()->toolbox()->linkManager()->disconnectAll(); qgcApp()->restoreOverrideCursor(); + qgcApp()->toolbox()->linkManager()->disconnectAll(); } AirframeType::AirframeType(const QString& name, const QString& imageResource, QObject* parent) : diff --git a/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml b/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml index 5dd0f980caabef13f91f11fcf49c821b36815852..1adf6162fa099f12ecaf13f17d0ab02c23fa70e8 100644 --- a/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/AirframeComponentSummary.qml @@ -37,7 +37,7 @@ FactPanel { VehicleSummaryRow { labelText: qsTr("Firmware Version:") - valueText: activeVehicle.firmwareMajorVersion == -1 ? qsTr("Unknown") : activeVehicle.firmwareMajorVersion + "." + activeVehicle.firmwareMinorVersion + "." + activeVehicle.firmwarePatchVersion + activeVehicle.firmwareVersionTypeString + valueText: activeVehicle.firmwareMajorVersion === -1 ? qsTr("Unknown") : activeVehicle.firmwareMajorVersion + "." + activeVehicle.firmwareMinorVersion + "." + activeVehicle.firmwarePatchVersion + activeVehicle.firmwareVersionTypeString } } } diff --git a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml index eddd43f080fbe94bebbc6ea78b93e8a5f3c52a0f..adcbccdc4720a5e3b2f10291e73bdd4302ee6c57 100644 --- a/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml +++ b/src/AutoPilotPlugins/PX4/AirframeFactMetaData.xml @@ -354,9 +354,9 @@ James Goppert <james.goppert@gmail.com> Quadrotor x - + Copter - Lucas de Marchi + Beat Kueng <beat@px4.io> Quadrotor x @@ -589,15 +589,6 @@ throttle - - - Tool - Julian Oes <julian@oes.ch> -This startup can be used on Pixhawk/Pixfalcon/Pixracer for the -passthrough of RC input and PWM output. - custom - - VTOL diff --git a/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml b/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml index e58bc7e97c21077d19c1ee35cb0f4a2b95bf7138..3755d61a2197ddc7e2840117d019a514527fd01b 100644 --- a/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml +++ b/src/AutoPilotPlugins/PX4/FlightModesComponentSummary.qml @@ -18,7 +18,7 @@ FactPanel { property Fact _nullFact property Fact _rcMapFltmode: controller.parameterExists(-1, "RC_MAP_FLTMODE") ? controller.getParameterFact(-1, "RC_MAP_FLTMODE") : _nullFact property Fact _rcMapModeSw: controller.getParameterFact(-1, "RC_MAP_MODE_SW") - property bool _simpleMode: _rcMapFltmode.value > 0 || _rcMapModeSw.value == 0 + property bool _simpleMode: _rcMapFltmode.value > 0 || _rcMapModeSw.value === 0 Loader { anchors.fill: parent diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc index c823235662febba594ac1ccd2b93ed8edacdacb5..ca98155a1c91e99a7d95c688349c16aea71a3de9 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.cc @@ -66,16 +66,16 @@ const QVariantList& PX4AutoPilotPlugin::vehicleComponents(void) _airframeComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_airframeComponent)); - _radioComponent = new PX4RadioComponent(_vehicle, this); - _radioComponent->setupTriggerSignals(); - _components.append(QVariant::fromValue((VehicleComponent*)_radioComponent)); - if (!_vehicle->hilMode()) { _sensorsComponent = new SensorsComponent(_vehicle, this); _sensorsComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_sensorsComponent)); } + _radioComponent = new PX4RadioComponent(_vehicle, this); + _radioComponent->setupTriggerSignals(); + _components.append(QVariant::fromValue((VehicleComponent*)_radioComponent)); + _flightModesComponent = new FlightModesComponent(_vehicle, this); _flightModesComponent->setupTriggerSignals(); _components.append(QVariant::fromValue((VehicleComponent*)_flightModesComponent)); diff --git a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h index fb1c3cc8aad9bb55e2085268af7356331e497bd1..253853b58d50f75a965eba855a21a150dd658194 100644 --- a/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h +++ b/src/AutoPilotPlugins/PX4/PX4AutoPilotPlugin.h @@ -44,6 +44,7 @@ public: const QVariantList& vehicleComponents(void) override; void parametersReadyPreChecks(void) override; QString prerequisiteSetup(VehicleComponent* component) const override; + protected: bool _incorrectParameterVersion; ///< true: parameter version incorrect, setup not allowed PX4AirframeLoader* _airframeFacts; @@ -58,6 +59,7 @@ protected: MotorComponent* _motorComponent; PX4TuningComponent* _tuningComponent; SyslinkComponent* _syslinkComponent; + private: QVariantList _components; }; diff --git a/src/AutoPilotPlugins/PX4/PX4FlightModes.qml b/src/AutoPilotPlugins/PX4/PX4FlightModes.qml index 68472e8f37340a5d7fba13f28dcce7608c7aa8c6..952d81fa2e1e4982916c02070686fd71f5886a92 100644 --- a/src/AutoPilotPlugins/PX4/PX4FlightModes.qml +++ b/src/AutoPilotPlugins/PX4/PX4FlightModes.qml @@ -39,7 +39,7 @@ SetupPage { property bool _rcMapFltmodeExists: controller.parameterExists(-1, "RC_MAP_FLTMODE") property Fact _rcMapFltmode: _rcMapFltmodeExists ? controller.getParameterFact(-1, "RC_MAP_FLTMODE") : _nullFact property Fact _rcMapModeSw: controller.getParameterFact(-1, "RC_MAP_MODE_SW") - property bool _simpleMode: _rcMapFltmodeExists ? _rcMapFltmode.value > 0 || _rcMapModeSw.value == 0 : false + property bool _simpleMode: _rcMapFltmodeExists ? _rcMapFltmode.value > 0 || _rcMapModeSw.value === 0 : false FactPanelController { id: controller diff --git a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml index 78d057646314e3cb05e14639bdd22e04674b40cc..48e3f8cb1c4ad7c6e69ae92a2251fe391c8c06fd 100644 --- a/src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml +++ b/src/AutoPilotPlugins/PX4/PX4SimpleFlightModes.qml @@ -145,7 +145,7 @@ Item { QGCLabel { anchors.baseline: returnCombo.baseline text: qsTr("Return switch:") - color: parent.fact.value == 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) + color: parent.fact.value === 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) } FactComboBox { @@ -164,7 +164,7 @@ Item { QGCLabel { anchors.baseline: killCombo.baseline text: qsTr("Kill switch:") - color: parent.fact.value == 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) + color: parent.fact.value === 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) } FactComboBox { @@ -183,7 +183,7 @@ Item { QGCLabel { anchors.baseline: offboardCombo.baseline text: qsTr("Offboard switch:") - color: parent.fact.value == 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) + color: parent.fact.value === 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) } FactComboBox { @@ -203,7 +203,7 @@ Item { QGCLabel { anchors.baseline: vtolCombo.baseline text: qsTr("VTOL mode switch:") - color: parent.fact.value == 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) + color: parent.fact.value === 0 ? qgcPal.text : (controller.rcChannelValues[parent.fact.value - 1] >= 1500 ? "yellow" : qgcPal.text) } FactComboBox { diff --git a/src/AutoPilotPlugins/PX4/PowerComponent.qml b/src/AutoPilotPlugins/PX4/PowerComponent.qml index e7b05cb1c6e68703e2339bf35b2401b4a2105f00..ef0f665be9cfbd1b18fb417120bf14495ec207db 100644 --- a/src/AutoPilotPlugins/PX4/PowerComponent.qml +++ b/src/AutoPilotPlugins/PX4/PowerComponent.qml @@ -43,24 +43,24 @@ SetupPage { readonly property string highlightPrefix: "" readonly property string highlightSuffix: "" + function getBatteryImage() + { + switch(battNumCells.value) { + case 1: return "/qmlimages/PowerComponentBattery_01cell.svg"; + case 2: return "/qmlimages/PowerComponentBattery_02cell.svg" + case 3: return "/qmlimages/PowerComponentBattery_03cell.svg" + case 4: return "/qmlimages/PowerComponentBattery_04cell.svg" + case 5: return "/qmlimages/PowerComponentBattery_05cell.svg" + case 6: return "/qmlimages/PowerComponentBattery_06cell.svg" + default: return "/qmlimages/PowerComponentBattery_01cell.svg"; + } + } + ColumnLayout { id: innerColumn anchors.horizontalCenter: parent.horizontalCenter spacing: ScreenTools.defaultFontPixelHeight - function getBatteryImage() - { - switch(battNumCells.value) { - case 1: return "/qmlimages/PowerComponentBattery_01cell.svg"; - case 2: return "/qmlimages/PowerComponentBattery_02cell.svg" - case 3: return "/qmlimages/PowerComponentBattery_03cell.svg" - case 4: return "/qmlimages/PowerComponentBattery_04cell.svg" - case 5: return "/qmlimages/PowerComponentBattery_05cell.svg" - case 6: return "/qmlimages/PowerComponentBattery_06cell.svg" - default: return "/qmlimages/PowerComponentBattery_01cell.svg"; - } - } - function drawArrowhead(ctx, x, y, radians) { ctx.save(); @@ -143,7 +143,7 @@ SetupPage { onClicked: { var measuredVoltageValue = parseFloat(measuredVoltage.text) - if (measuredVoltageValue == 0 || isNaN(measuredVoltageValue)) { + if (measuredVoltageValue === 0 || isNaN(measuredVoltageValue)) { return } var newVoltageDivider = (measuredVoltageValue * battVoltageDivider.value) / controller.vehicle.battery.voltage.value @@ -201,7 +201,7 @@ SetupPage { onClicked: { var measuredCurrentValue = parseFloat(measuredCurrent.text) - if (measuredCurrentValue == 0) { + if (measuredCurrentValue === 0) { return } var newAmpsPerVolt = (measuredCurrentValue * battAmpsPerVolt.value) / controller.vehicle.battery.current.value @@ -379,7 +379,7 @@ SetupPage { QGCCheckBox { id: showUAVCAN text: qsTr("Show UAVCAN Settings") - checked: uavcanEnable.rawValue != 0 + checked: uavcanEnable ? uavcanEnable.rawValue !== 0 : false } QGCGroupBox { diff --git a/src/AutoPilotPlugins/PX4/SafetyComponent.qml b/src/AutoPilotPlugins/PX4/SafetyComponent.qml index 59e07288bc75c987089d7e473819671ab4fdf94d..4b9e00dca7d2753cbce925dd1066d3e58c61dc9c 100644 --- a/src/AutoPilotPlugins/PX4/SafetyComponent.qml +++ b/src/AutoPilotPlugins/PX4/SafetyComponent.qml @@ -167,10 +167,10 @@ SetupPage { columns: 3 Image { - mipmap: true - fillMode: Image.PreserveAspectFit - source: qgcPal.globalTheme === qgcPal.Light ? "/qmlimages/LowBatteryLight.svg" : "/qmlimages/LowBattery.svg" - Layout.rowSpan: 3 + mipmap: true + fillMode: Image.PreserveAspectFit + source: qgcPal.globalTheme === qgcPal.Light ? "/qmlimages/LowBatteryLight.svg" : "/qmlimages/LowBattery.svg" + Layout.rowSpan: 4 Layout.maximumWidth: _imageWidth Layout.maximumHeight: _imageHeight width: _imageWidth @@ -204,6 +204,15 @@ SetupPage { fact: controller.getParameterFact(-1, "BAT_CRIT_THR") Layout.minimumWidth: _editFieldWidth } + + QGCLabel { + text: qsTr("Battery Emergency Level:") + Layout.fillWidth: true + } + FactTextField { + fact: controller.getParameterFact(-1, "BAT_EMERGEN_THR") + Layout.minimumWidth: _editFieldWidth + } } Item { width: 1; height: _margins; Layout.columnSpan: 3 } diff --git a/src/AutoPilotPlugins/PX4/SensorsComponent.cc b/src/AutoPilotPlugins/PX4/SensorsComponent.cc index 60f0758f13aabea41a9ea42635f11df5e0a0cc76..7fbc76bd013d936ff7bd053019a5843d5c77a6c0 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponent.cc +++ b/src/AutoPilotPlugins/PX4/SensorsComponent.cc @@ -16,8 +16,9 @@ #include "QGCQmlWidgetHolder.h" #include "SensorsComponentController.h" -const char* SensorsComponent::_airspeedBreaker = "CBRK_AIRSPD_CHK"; -const char* SensorsComponent::_airspeedCal = "SENS_DPRES_OFF"; +const char* SensorsComponent::_airspeedBreakerParam = "CBRK_AIRSPD_CHK"; +const char* SensorsComponent::_airspeedDisabledParam = "FW_ARSP_MODE"; +const char* SensorsComponent::_airspeedCalParam = "SENS_DPRES_OFF"; SensorsComponent::SensorsComponent(Vehicle* vehicle, AutoPilotPlugin* autopilot, QObject* parent) : VehicleComponent(vehicle, autopilot, parent), @@ -55,10 +56,10 @@ bool SensorsComponent::setupComplete(void) const } if (_vehicle->fixedWing() || _vehicle->vtol()) { - if (_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _airspeedBreaker)->rawValue().toInt() != 162128) { - if (_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _airspeedCal)->rawValue().toFloat() == 0.0f) { - return false; - } + if (!_vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _airspeedDisabledParam)->rawValue().toBool() && + _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _airspeedBreakerParam)->rawValue().toInt() != 162128 && + _vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, _airspeedCalParam)->rawValue().toFloat() == 0.0f) { + return false; } } @@ -71,7 +72,7 @@ QStringList SensorsComponent::setupCompleteChangedTriggerList(void) const triggers << _deviceIds; if (_vehicle->fixedWing() || _vehicle->vtol()) { - triggers << _airspeedCal << _airspeedBreaker; + triggers << _airspeedCalParam << _airspeedBreakerParam; } return triggers; diff --git a/src/AutoPilotPlugins/PX4/SensorsComponent.h b/src/AutoPilotPlugins/PX4/SensorsComponent.h index da29a3848d298ccf6e4c89e9cec99daf66d07e38..9201c5e8e8dd0995284428bef57876ae607a160f 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponent.h +++ b/src/AutoPilotPlugins/PX4/SensorsComponent.h @@ -41,8 +41,9 @@ private: QVariantList _summaryItems; QStringList _deviceIds; - static const char* _airspeedBreaker; - static const char* _airspeedCal; + static const char* _airspeedDisabledParam; + static const char* _airspeedBreakerParam; + static const char* _airspeedCalParam; }; #endif diff --git a/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml b/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml index 760a75f9bd30edf6ef9636161ba77b8fc850e868..4670bbe3d4da60666096b9fceb9b3c2438194d35 100644 --- a/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml +++ b/src/AutoPilotPlugins/PX4/SensorsComponentSummaryFixedWing.qml @@ -18,10 +18,15 @@ FactPanel { QGCPalette { id: qgcPal; colorGroupEnabled: enabled } FactPanelController { id: controller; factPanel: panel } - property Fact mag0IdFact: controller.getParameterFact(-1, "CAL_MAG0_ID") - property Fact gyro0IdFact: controller.getParameterFact(-1, "CAL_GYRO0_ID") - property Fact accel0IdFact: controller.getParameterFact(-1, "CAL_ACC0_ID") - property Fact dpressOffFact: controller.getParameterFact(-1, "SENS_DPRES_OFF") + property Fact mag0IdFact: controller.getParameterFact(-1, "CAL_MAG0_ID") + property Fact gyro0IdFact: controller.getParameterFact(-1, "CAL_GYRO0_ID") + property Fact accel0IdFact: controller.getParameterFact(-1, "CAL_ACC0_ID") + property Fact dpressOffFact: controller.getParameterFact(-1, "SENS_DPRES_OFF") + property Fact airspeedDisabledFact: controller.getParameterFact(-1, "FW_ARSP_MODE") + property Fact airspeedBreakerFact: controller.getParameterFact(-1, "CBRK_AIRSPD_CHK") + + property bool _airspeedVisible: airspeedDisabledFact.value === false && airspeedBreakerFact.value !== 162128 + property bool _airspeedCalRequired: _airspeedVisible && dpressOffFact.value === 0 Column { anchors.fill: parent @@ -42,8 +47,9 @@ FactPanel { } VehicleSummaryRow { - labelText: qsTr("Airspeed:") - valueText: dpressOffFact ? (dpressOffFact.value === 0 ? qsTr("Setup required") : qsTr("Ready")) : "" + labelText: qsTr("Airspeed:") + visible: _airspeedVisible + valueText: _airspeedCalRequired ? qsTr("Setup required") : qsTr("Ready") } } } diff --git a/src/AutoPilotPlugins/PX4/SensorsSetup.qml b/src/AutoPilotPlugins/PX4/SensorsSetup.qml index 157dcd4da92eb2a7a9135d496b85601d1b8f6ce5..fdbca17573a97decdf398d44dd6875cddf8adcac 100644 --- a/src/AutoPilotPlugins/PX4/SensorsSetup.qml +++ b/src/AutoPilotPlugins/PX4/SensorsSetup.qml @@ -42,9 +42,9 @@ Item { readonly property string boardRotationText: qsTr("If the orientation is in the direction of flight, select ROTATION_NONE.") readonly property string compassRotationText: qsTr("If the orientation is in the direction of flight, select ROTATION_NONE.") - readonly property string compassHelp: qsTr("For Compass calibration you will need to rotate your vehicle through a number of positions. Click Ok to start calibration.") - readonly property string gyroHelp: qsTr("For Gyroscope calibration you will need to place your vehicle on a surface and leave it still. Click Ok to start calibration.") - readonly property string accelHelp: qsTr("For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds. Click Ok to start calibration.") + readonly property string compassHelp: qsTr("For Compass calibration you will need to rotate your vehicle through a number of positions.\n\nClick Ok to start calibration.") + readonly property string gyroHelp: qsTr("For Gyroscope calibration you will need to place your vehicle on a surface and leave it still.\n\nClick Ok to start calibration.") + readonly property string accelHelp: qsTr("For Accelerometer calibration you will need to place your vehicle on all six sides on a perfectly level surface and hold it still in each orientation for a few seconds.\n\nClick Ok to start calibration.") readonly property string levelHelp: qsTr("To level the horizon you need to place the vehicle in its level flight position and press OK.") readonly property string airspeedHelp: qsTr("For Airspeed calibration you will need to keep your airspeed sensor out of any wind and then blow across the sensor. Do not touch the sensor or obstruct any holes during the calibration.") @@ -177,15 +177,10 @@ Item { Column { anchors.fill: parent - spacing: ScreenTools.defaultFontPixelWidth / 2 - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - text: preCalibrationDialogHelp - } + spacing: ScreenTools.defaultFontPixelHeight Column { + width: parent.width spacing: 5 visible: !_sensorsHaveFixedOrientation @@ -194,14 +189,13 @@ Item { width: parent.width wrapMode: Text.WordWrap visible: (preCalibrationDialogType != "airspeed") && (preCalibrationDialogType != "gyro") - text: boardRotationText + text: qsTr("Set autopilot orientation before calibrating.") } Column { visible: boardRotationHelp.visible - QGCLabel { - text: qsTr("Autopilot Orientation:") - } + + QGCLabel { text: qsTr("Autopilot Orientation:") } FactComboBox { id: boardRotationCombo @@ -211,6 +205,12 @@ Item { } } } + + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + text: preCalibrationDialogHelp + } } } } @@ -352,7 +352,7 @@ Item { id: compassButton width: _buttonWidth text: qsTr("Compass") - indicatorGreen: cal_mag0_id.value != 0 + indicatorGreen: cal_mag0_id.value !== 0 visible: QGroundControl.corePlugin.options.showSensorCalibrationCompass && showSensorCalibrationCompass onClicked: { @@ -366,7 +366,7 @@ Item { id: gyroButton width: _buttonWidth text: qsTr("Gyroscope") - indicatorGreen: cal_gyro0_id.value != 0 + indicatorGreen: cal_gyro0_id.value !== 0 visible: QGroundControl.corePlugin.options.showSensorCalibrationGyro && showSensorCalibrationGyro onClicked: { @@ -380,7 +380,7 @@ Item { id: accelButton width: _buttonWidth text: qsTr("Accelerometer") - indicatorGreen: cal_acc0_id.value != 0 + indicatorGreen: cal_acc0_id.value !== 0 visible: QGroundControl.corePlugin.options.showSensorCalibrationAccel && showSensorCalibrationAccel onClicked: { @@ -394,8 +394,8 @@ Item { id: levelButton width: _buttonWidth text: qsTr("Level Horizon") - indicatorGreen: sens_board_x_off.value != 0 || sens_board_y_off != 0 | sens_board_z_off != 0 - enabled: cal_acc0_id.value != 0 && cal_gyro0_id.value != 0 + indicatorGreen: sens_board_x_off.value !== 0 || sens_board_y_off.value !== 0 | sens_board_z_off.value !== 0 + enabled: cal_acc0_id.value !== 0 && cal_gyro0_id.value !== 0 visible: QGroundControl.corePlugin.options.showSensorCalibrationLevel && showSensorCalibrationLevel onClicked: { @@ -410,10 +410,11 @@ Item { width: _buttonWidth text: qsTr("Airspeed") visible: (controller.vehicle.fixedWing || controller.vehicle.vtol) && - controller.getParameterFact(-1, "CBRK_AIRSPD_CHK").value != 162128 && + controller.getParameterFact(-1, "FW_ARSP_MODE").value === false && + controller.getParameterFact(-1, "CBRK_AIRSPD_CHK").value !== 162128 && QGroundControl.corePlugin.options.showSensorCalibrationAirspeed && showSensorCalibrationAirspeed - indicatorGreen: sens_dpres_off.value != 0 + indicatorGreen: sens_dpres_off.value !== 0 onClicked: { preCalibrationDialogType = "airspeed" diff --git a/src/Camera/QGCCameraControl.cc b/src/Camera/QGCCameraControl.cc index ca491fd27401d7b0745b465b96497d3409bd9eef..d2cfe911cff25310d33e873a5c2c5f8a57eec9a7 100644 --- a/src/Camera/QGCCameraControl.cc +++ b/src/Camera/QGCCameraControl.cc @@ -40,6 +40,7 @@ static const char* kParameterrange = "parameterrange"; static const char* kParameterranges = "parameterranges"; static const char* kParameters = "parameters"; static const char* kReadOnly = "readonly"; +static const char* kWriteOnly = "writeonly"; static const char* kRoption = "roption"; static const char* kStep = "step"; static const char* kStrings = "strings"; @@ -659,6 +660,13 @@ QGCCameraControl::_loadSettings(const QDomNodeList nodeList) //-- Is it read only? bool readOnly = false; read_attribute(parameterNode, kReadOnly, readOnly); + //-- Is it write only? + bool writeOnly = false; + read_attribute(parameterNode, kWriteOnly, writeOnly); + //-- It can't be both + if(readOnly && writeOnly) { + qCritical() << QString("Parameter %1 cannot be both read only and write only").arg(factName); + } //-- Param type bool unknownType; FactMetaData::ValueType_t factType = FactMetaData::stringToType(type, unknownType); @@ -689,6 +697,7 @@ QGCCameraControl::_loadSettings(const QDomNodeList nodeList) metaData->setLongDescription(description); metaData->setHasControl(control); metaData->setReadOnly(readOnly); + metaData->setWriteOnly(writeOnly); //-- Options (enums) QDomElement optionElem = parameterNode.toElement(); QDomNodeList optionsRoot = optionElem.elementsByTagName(kOptions); @@ -797,7 +806,7 @@ QGCCameraControl::_loadSettings(const QDomNodeList nodeList) metaData->setRawUnits(attr); } } - qCDebug(CameraControlLog) << "New parameter:" << factName; + qCDebug(CameraControlLog) << "New parameter:" << factName << (readOnly ? "ReadOnly" : "Writable") << (writeOnly ? "WriteOnly" : "Readable"); _nameToFactMetaDataMap[factName] = metaData; Fact* pFact = new Fact(_compID, factName, factType, this); QQmlEngine::setObjectOwnership(pFact, QQmlEngine::CppOwnership); diff --git a/src/Camera/QGCCameraControl.h b/src/Camera/QGCCameraControl.h index 307934ba230d8d9748c3a29df35045d639b58112..c4f93152b4787797b033a07bdd1f39dd498ff436 100644 --- a/src/Camera/QGCCameraControl.h +++ b/src/Camera/QGCCameraControl.h @@ -202,16 +202,16 @@ protected: virtual void _setCameraMode (CameraMode mode); protected slots: - void _initWhenReady (); - void _requestCameraSettings (); - void _requestAllParameters (); - void _requestParamUpdates (); - void _requestCaptureStatus (); - void _requestStorageInfo (); - void _downloadFinished (); - void _mavCommandResult (int vehicleId, int component, int command, int result, bool noReponseFromVehicle); - void _dataReady (QByteArray data); - void _paramDone (); + virtual void _initWhenReady (); + virtual void _requestCameraSettings (); + virtual void _requestAllParameters (); + virtual void _requestParamUpdates (); + virtual void _requestCaptureStatus (); + virtual void _requestStorageInfo (); + virtual void _downloadFinished (); + virtual void _mavCommandResult (int vehicleId, int component, int command, int result, bool noReponseFromVehicle); + virtual void _dataReady (QByteArray data); + virtual void _paramDone (); private: bool _handleLocalization (QByteArray& bytes); diff --git a/src/Camera/QGCCameraIO.cc b/src/Camera/QGCCameraIO.cc index 577c0207d0d9b70a8f6a6294825bf328edd31749..a8d4130251ea22a2b71e10aa466501ed1b0ecb84 100644 --- a/src/Camera/QGCCameraIO.cc +++ b/src/Camera/QGCCameraIO.cc @@ -28,8 +28,13 @@ QGCCameraParamIO::QGCCameraParamIO(QGCCameraControl *control, Fact* fact, Vehicl _paramWriteTimer.setInterval(3000); _paramRequestTimer.setSingleShot(true); _paramRequestTimer.setInterval(3500); + if(_fact->writeOnly()) { + //-- Write mode is always "done" as it won't ever read + _done = true; + } else { + connect(&_paramRequestTimer, &QTimer::timeout, this, &QGCCameraParamIO::_paramRequestTimeout); + } connect(&_paramWriteTimer, &QTimer::timeout, this, &QGCCameraParamIO::_paramWriteTimeout); - connect(&_paramRequestTimer, &QTimer::timeout, this, &QGCCameraParamIO::_paramRequestTimeout); connect(_fact, &Fact::rawValueChanged, this, &QGCCameraParamIO::_factChanged); connect(_fact, &Fact::_containerRawValueChanged, this, &QGCCameraParamIO::_containerRawValueChanged); _pMavlink = qgcApp()->toolbox()->mavlinkProtocol(); @@ -38,31 +43,42 @@ QGCCameraParamIO::QGCCameraParamIO(QGCCameraControl *control, Fact* fact, Vehicl switch (_fact->type()) { case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeBool: - _mavParamType = MAV_PARAM_TYPE_UINT8; + _mavParamType = MAV_PARAM_EXT_TYPE_UINT8; break; case FactMetaData::valueTypeInt8: - _mavParamType = MAV_PARAM_TYPE_INT8; + _mavParamType = MAV_PARAM_EXT_TYPE_INT8; break; case FactMetaData::valueTypeUint16: - _mavParamType = MAV_PARAM_TYPE_UINT16; + _mavParamType = MAV_PARAM_EXT_TYPE_UINT16; break; case FactMetaData::valueTypeInt16: - _mavParamType = MAV_PARAM_TYPE_INT16; + _mavParamType = MAV_PARAM_EXT_TYPE_INT16; break; case FactMetaData::valueTypeUint32: - _mavParamType = MAV_PARAM_TYPE_UINT32; + _mavParamType = MAV_PARAM_EXT_TYPE_UINT32; + break; + case FactMetaData::valueTypeUint64: + _mavParamType = MAV_PARAM_EXT_TYPE_UINT64; + break; + case FactMetaData::valueTypeInt64: + _mavParamType = MAV_PARAM_EXT_TYPE_INT64; break; case FactMetaData::valueTypeFloat: - _mavParamType = MAV_PARAM_TYPE_REAL32; + _mavParamType = MAV_PARAM_EXT_TYPE_REAL32; break; + case FactMetaData::valueTypeDouble: + _mavParamType = MAV_PARAM_EXT_TYPE_REAL64; + break; + //-- String and custom are the same for now + case FactMetaData::valueTypeString: case FactMetaData::valueTypeCustom: - _mavParamType = (MAV_PARAM_TYPE)11; // MAV_PARAM_TYPE_CUSTOM; + _mavParamType = MAV_PARAM_EXT_TYPE_CUSTOM; break; default: qWarning() << "Unsupported fact type" << _fact->type() << "for" << _fact->name(); // Fall through case FactMetaData::valueTypeInt32: - _mavParamType = MAV_PARAM_TYPE_INT32; + _mavParamType = MAV_PARAM_EXT_TYPE_INT32; break; } } @@ -71,9 +87,11 @@ QGCCameraParamIO::QGCCameraParamIO(QGCCameraControl *control, Fact* fact, Vehicl void QGCCameraParamIO::setParamRequest() { - _paramRequestReceived = false; - _requestRetries = 0; - _paramRequestTimer.start(); + if(!_fact->writeOnly()) { + _paramRequestReceived = false; + _requestRetries = 0; + _paramRequestTimer.start(); + } } //----------------------------------------------------------------------------- @@ -136,9 +154,20 @@ QGCCameraParamIO::_sendParameter() case FactMetaData::valueTypeUint32: union_value.param_uint32 = (uint32_t)_fact->rawValue().toUInt(); break; + case FactMetaData::valueTypeInt64: + union_value.param_int64 = (int64_t)_fact->rawValue().toLongLong(); + break; + case FactMetaData::valueTypeUint64: + union_value.param_uint64 = (uint64_t)_fact->rawValue().toULongLong(); + break; case FactMetaData::valueTypeFloat: union_value.param_float = _fact->rawValue().toFloat(); break; + case FactMetaData::valueTypeDouble: + union_value.param_double = _fact->rawValue().toDouble(); + break; + //-- String and custom are the same for now + case FactMetaData::valueTypeString: case FactMetaData::valueTypeCustom: { QByteArray custom = _fact->rawValue().toByteArray(); @@ -250,28 +279,34 @@ QGCCameraParamIO::_valueFromMessage(const char* value, uint8_t param_type) param_ext_union_t u; memcpy(u.bytes, value, MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN); switch (param_type) { - case MAV_PARAM_TYPE_REAL32: + case MAV_PARAM_EXT_TYPE_REAL32: var = QVariant(u.param_float); break; - case MAV_PARAM_TYPE_UINT8: + case MAV_PARAM_EXT_TYPE_UINT8: var = QVariant(u.param_uint8); break; - case MAV_PARAM_TYPE_INT8: + case MAV_PARAM_EXT_TYPE_INT8: var = QVariant(u.param_int8); break; - case MAV_PARAM_TYPE_UINT16: + case MAV_PARAM_EXT_TYPE_UINT16: var = QVariant(u.param_uint16); break; - case MAV_PARAM_TYPE_INT16: + case MAV_PARAM_EXT_TYPE_INT16: var = QVariant(u.param_int16); break; - case MAV_PARAM_TYPE_UINT32: + case MAV_PARAM_EXT_TYPE_UINT32: var = QVariant(u.param_uint32); break; - case MAV_PARAM_TYPE_INT32: + case MAV_PARAM_EXT_TYPE_INT32: var = QVariant(u.param_int32); break; - case 11: //MAV_PARAM_TYPE_CUSTOM: + case MAV_PARAM_EXT_TYPE_UINT64: + var = QVariant((qulonglong)u.param_uint64); + break; + case MAV_PARAM_EXT_TYPE_INT64: + var = QVariant((qlonglong)u.param_int64); + break; + case MAV_PARAM_EXT_TYPE_CUSTOM: var = QVariant(QByteArray(value, MAVLINK_MSG_PARAM_EXT_SET_FIELD_PARAM_VALUE_LEN)); break; default: @@ -303,6 +338,14 @@ QGCCameraParamIO::_paramRequestTimeout() void QGCCameraParamIO::paramRequest(bool reset) { + //-- If it's write only, we don't request it. + if(_fact->writeOnly()) { + if(!_done) { + _done = true; + _control->_paramDone(); + } + return; + } if(reset) { _requestRetries = 0; _forceUIUpdate = true; diff --git a/src/Camera/QGCCameraIO.h b/src/Camera/QGCCameraIO.h index a3f4d57a2e1a62c14639b29f534fb34875e022b0..236ef6fe40ed235bc84509fc208a2383c403f7b5 100644 --- a/src/Camera/QGCCameraIO.h +++ b/src/Camera/QGCCameraIO.h @@ -19,6 +19,9 @@ MAVPACKED( typedef struct { union { float param_float; + double param_double; + int64_t param_int64; + uint64_t param_uint64; int32_t param_int32; uint32_t param_uint32; int16_t param_int16; @@ -67,7 +70,7 @@ private: QTimer _paramRequestTimer; bool _done; bool _updateOnSet; - MAV_PARAM_TYPE _mavParamType; + MAV_PARAM_EXT_TYPE _mavParamType; MAVLinkProtocol* _pMavlink; bool _forceUIUpdate; }; diff --git a/src/Camera/QGCCameraManager.h b/src/Camera/QGCCameraManager.h index 9817c574ed566a2d38213e4b3ff2a7b1e7a04383..e443701e6320d7685d33346177bdd026adc1ea23 100644 --- a/src/Camera/QGCCameraManager.h +++ b/src/Camera/QGCCameraManager.h @@ -25,28 +25,28 @@ public: QGCCameraManager(Vehicle* vehicle); virtual ~QGCCameraManager(); - Q_PROPERTY(QmlObjectListModel* cameras READ cameras NOTIFY camerasChanged) + Q_PROPERTY(QmlObjectListModel* cameras READ cameras NOTIFY camerasChanged) //-- Return a list of cameras provided by this vehicle - virtual QmlObjectListModel* cameras () { return &_cameras; } + virtual QmlObjectListModel* cameras () { return &_cameras; } signals: - void camerasChanged (); + void camerasChanged (); protected slots: - void _vehicleReady (bool ready); - void _mavlinkMessageReceived (const mavlink_message_t& message); + virtual void _vehicleReady (bool ready); + virtual void _mavlinkMessageReceived (const mavlink_message_t& message); protected: - QGCCameraControl* _findCamera (int id); - void _requestCameraInfo (int compID); - 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); + virtual QGCCameraControl* _findCamera (int id); + virtual void _requestCameraInfo (int compID); + 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); protected: Vehicle* _vehicle; diff --git a/src/FactSystem/Fact.cc b/src/FactSystem/Fact.cc index 83fe83e9223ba126e333c3ea5a1a460e086e642f..3afce178fd287c5a40d106ffde28be1d9a1b8c1d 100644 --- a/src/FactSystem/Fact.cc +++ b/src/FactSystem/Fact.cc @@ -7,12 +7,10 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - #include "Fact.h" #include "QGCMAVLink.h" +#include "QGCApplication.h" +#include "QGCCorePlugin.h" #include #include @@ -50,6 +48,21 @@ Fact::Fact(int componentId, QString name, FactMetaData::ValueType_t type, QObjec QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); } +Fact::Fact(FactMetaData* metaData, QObject* parent) + : QObject(parent) + , _name (metaData->name()) + , _componentId (0) + , _rawValue (0) + , _type (metaData->type()) + , _metaData (NULL) + , _sendValueChangedSignals (true) + , _deferredValueChangeSignal(false) +{ + // Allow core plugin a chance to override the default value + qgcApp()->toolbox()->corePlugin()->adjustSettingMetaData(*metaData); + setMetaData(metaData, true /* setDefaultFromMetaData */); +} + Fact::Fact(const Fact& other, QObject* parent) : QObject(parent) { @@ -148,9 +161,11 @@ void Fact::_containerSetRawValue(const QVariant& value) if(_rawValue != value) { _rawValue = value; _sendValueChangedSignal(cookedValue()); - emit vehicleUpdated(_rawValue); emit rawValueChanged(_rawValue); } + + // This always need to be signalled in order to support forceSetRawValue usage and waiting for vehicleUpdated signal + emit vehicleUpdated(_rawValue); } QString Fact::name(void) const @@ -484,6 +499,16 @@ int Fact::decimalPlaces(void) const } } +QString Fact::category(void) const +{ + if (_metaData) { + return _metaData->category(); + } else { + qWarning() << kMissingMetadata << name(); + return QString(); + } +} + QString Fact::group(void) const { if (_metaData) { @@ -637,3 +662,23 @@ bool Fact::readOnly(void) const return false; } } + +bool Fact::writeOnly(void) const +{ + if (_metaData) { + return _metaData->writeOnly(); + } else { + qWarning() << kMissingMetadata << name(); + return false; + } +} + +bool Fact::volatileValue(void) const +{ + if (_metaData) { + return _metaData->volatileValue(); + } else { + qWarning() << kMissingMetadata << name(); + return false; + } +} diff --git a/src/FactSystem/Fact.h b/src/FactSystem/Fact.h index 63ee9a584dbc13d1aecd13ba05632bc6d7cac284..cc0336ee80de34e289b58ae89fc77d5a51bc86c5 100644 --- a/src/FactSystem/Fact.h +++ b/src/FactSystem/Fact.h @@ -31,6 +31,10 @@ public: Fact(int componentId, QString name, FactMetaData::ValueType_t type, QObject* parent = NULL); Fact(const Fact& other, QObject* parent = NULL); + /// Creates a Fact using the name and type from metaData. Also calls QGCCorePlugin::adjustSettingsMetaData allowing + /// custom builds to override the metadata. + Fact(FactMetaData* metaData, QObject* parent = NULL); + const Fact& operator=(const Fact& other); Q_PROPERTY(int componentId READ componentId CONSTANT) @@ -44,6 +48,7 @@ public: Q_PROPERTY(QStringList enumStrings READ enumStrings NOTIFY enumsChanged) Q_PROPERTY(QString enumStringValue READ enumStringValue WRITE setEnumStringValue NOTIFY valueChanged) Q_PROPERTY(QVariantList enumValues READ enumValues NOTIFY enumsChanged) + Q_PROPERTY(QString category READ category CONSTANT) Q_PROPERTY(QString group READ group CONSTANT) Q_PROPERTY(QString longDescription READ longDescription CONSTANT) Q_PROPERTY(QVariant max READ cookedMax CONSTANT) @@ -66,6 +71,8 @@ public: Q_PROPERTY(bool typeIsBool READ typeIsBool CONSTANT) Q_PROPERTY(bool hasControl READ hasControl CONSTANT) Q_PROPERTY(bool readOnly READ readOnly CONSTANT) + Q_PROPERTY(bool writeOnly READ writeOnly CONSTANT) + Q_PROPERTY(bool volatileValue READ volatileValue CONSTANT) /// Convert and validate value /// @param convertOnly true: validate type conversion only, false: validate against meta data as well @@ -87,6 +94,7 @@ public: QStringList enumStrings (void) const; QString enumStringValue (void); // This is not const, since an unknown value can modify the enum lists QVariantList enumValues (void) const; + QString category (void) const; QString group (void) const; QString longDescription (void) const; QVariant rawMax (void) const; @@ -112,6 +120,8 @@ public: bool typeIsBool (void) const { return type() == FactMetaData::valueTypeBool; } bool hasControl (void) const; bool readOnly (void) const; + bool writeOnly (void) const; + bool volatileValue (void) const; /// Returns the values as a string with full 18 digit precision if float/double. QString rawValueStringFullPrecision(void) const; diff --git a/src/FactSystem/FactControls/FactBitmask.qml b/src/FactSystem/FactControls/FactBitmask.qml index 11efcba86fc5f1621082af8ef30ca732e7a76536..517ce6a4bf5a4b2361454e6117c3e0f95fbc02ac 100644 --- a/src/FactSystem/FactControls/FactBitmask.qml +++ b/src/FactSystem/FactControls/FactBitmask.qml @@ -33,10 +33,12 @@ Flow { checked: fact.value & fact.bitmaskValues[index] onClicked: { + var i; + var otherCheckbox; if (checked) { if (firstEntryIsAll && index == 0) { - for (var i=1; i::quiet_NaN()) - , _hasControl(true) - , _readOnly(false) -{ - + , _rawMax (_maxForType()) + , _maxIsDefaultForType (true) + , _rawMin (_minForType()) + , _minIsDefaultForType (true) + , _rawTranslator (_defaultTranslator) + , _cookedTranslator (_defaultTranslator) + , _rebootRequired (false) + , _increment (std::numeric_limits::quiet_NaN()) + , _hasControl (true) + , _readOnly (false) + , _writeOnly (false) + , _volatile (false) +{ + _category = kDefaultCategory; + _group = kDefaultGroup; } FactMetaData::FactMetaData(ValueType_t type, QObject* parent) - : QObject(parent) - , _type(type) - , _decimalPlaces(unknownDecimalPlaces) - , _rawDefaultValue(0) + : QObject (parent) + , _type (type) + , _decimalPlaces (unknownDecimalPlaces) + , _rawDefaultValue (0) , _defaultValueAvailable(false) - , _group("*Default Group") - , _rawMax(_maxForType()) - , _maxIsDefaultForType(true) - , _rawMin(_minForType()) - , _minIsDefaultForType(true) - , _rawTranslator(_defaultTranslator) - , _cookedTranslator(_defaultTranslator) - , _rebootRequired(false) - , _increment(std::numeric_limits::quiet_NaN()) - , _hasControl(true) - , _readOnly(false) -{ - + , _rawMax (_maxForType()) + , _maxIsDefaultForType (true) + , _rawMin (_minForType()) + , _minIsDefaultForType (true) + , _rawTranslator (_defaultTranslator) + , _cookedTranslator (_defaultTranslator) + , _rebootRequired (false) + , _increment (std::numeric_limits::quiet_NaN()) + , _hasControl (true) + , _readOnly (false) + , _writeOnly (false) + , _volatile (false) +{ + _category = kDefaultCategory; + _group = kDefaultGroup; } FactMetaData::FactMetaData(const FactMetaData& other, QObject* parent) @@ -122,25 +133,27 @@ FactMetaData::FactMetaData(const FactMetaData& other, QObject* parent) } FactMetaData::FactMetaData(ValueType_t type, const QString name, QObject* parent) - : QObject(parent) - , _type(type) - , _decimalPlaces(unknownDecimalPlaces) - , _rawDefaultValue(0) + : QObject (parent) + , _type (type) + , _decimalPlaces (unknownDecimalPlaces) + , _rawDefaultValue (0) , _defaultValueAvailable(false) - , _group("*Default Group") - , _rawMax(_maxForType()) - , _maxIsDefaultForType(true) - , _rawMin(_minForType()) - , _minIsDefaultForType(true) - , _name(name) - , _rawTranslator(_defaultTranslator) - , _cookedTranslator(_defaultTranslator) - , _rebootRequired(false) - , _increment(std::numeric_limits::quiet_NaN()) - , _hasControl(true) - , _readOnly(false) -{ - + , _rawMax (_maxForType()) + , _maxIsDefaultForType (true) + , _rawMin (_minForType()) + , _minIsDefaultForType (true) + , _name (name) + , _rawTranslator (_defaultTranslator) + , _cookedTranslator (_defaultTranslator) + , _rebootRequired (false) + , _increment (std::numeric_limits::quiet_NaN()) + , _hasControl (true) + , _readOnly (false) + , _writeOnly (false) + , _volatile (false) +{ + _category = kDefaultCategory; + _group = kDefaultGroup; } const FactMetaData& FactMetaData::operator=(const FactMetaData& other) @@ -152,6 +165,7 @@ const FactMetaData& FactMetaData::operator=(const FactMetaData& other) _bitmaskValues = other._bitmaskValues; _enumStrings = other._enumStrings; _enumValues = other._enumValues; + _category = other._category; _group = other._group; _longDescription = other._longDescription; _rawMax = other._rawMax; @@ -169,9 +183,21 @@ const FactMetaData& FactMetaData::operator=(const FactMetaData& other) _increment = other._increment; _hasControl = other._hasControl; _readOnly = other._readOnly; + _writeOnly = other._writeOnly; + _volatile = other._volatile; return *this; } +const QString FactMetaData::defaultCategory() +{ + return QString(kDefaultCategory); +} + +const QString FactMetaData::defaultGroup() +{ + return QString(kDefaultGroup); +} + QVariant FactMetaData::rawDefaultValue(void) const { if (_defaultValueAvailable) { @@ -228,9 +254,13 @@ QVariant FactMetaData::_minForType(void) const case valueTypeInt16: return QVariant(std::numeric_limits::min()); case valueTypeUint32: - return QVariant(std::numeric_limits::min()); + return QVariant(std::numeric_limits::min()); case valueTypeInt32: - return QVariant(std::numeric_limits::min()); + return QVariant(std::numeric_limits::min()); + case valueTypeUint64: + return QVariant((qulonglong)std::numeric_limits::min()); + case valueTypeInt64: + return QVariant((qlonglong)std::numeric_limits::min()); case valueTypeFloat: return QVariant(-std::numeric_limits::max()); case valueTypeDouble: @@ -244,7 +274,7 @@ QVariant FactMetaData::_minForType(void) const case valueTypeCustom: return QVariant(); } - + // Make windows compiler happy, even switch is full cased return QVariant(); } @@ -261,9 +291,13 @@ QVariant FactMetaData::_maxForType(void) const case valueTypeInt16: return QVariant(std::numeric_limits::max()); case valueTypeUint32: - return QVariant(std::numeric_limits::max()); + return QVariant(std::numeric_limits::max()); case valueTypeInt32: - return QVariant(std::numeric_limits::max()); + return QVariant(std::numeric_limits::max()); + case valueTypeUint64: + return QVariant((qulonglong)std::numeric_limits::max()); + case valueTypeInt64: + return QVariant((qlonglong)std::numeric_limits::max()); case valueTypeFloat: return QVariant(std::numeric_limits::max()); case valueTypeElapsedTimeInSeconds: @@ -276,7 +310,7 @@ QVariant FactMetaData::_maxForType(void) const case valueTypeCustom: return QVariant(); } - + // Make windows compiler happy, even switch is full cased return QVariant(); } @@ -284,9 +318,9 @@ QVariant FactMetaData::_maxForType(void) const bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertOnly, QVariant& typedValue, QString& errorString) { bool convertOk = false; - + errorString.clear(); - + switch (type()) { case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt16: @@ -294,7 +328,15 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO typedValue = QVariant(rawValue.toInt(&convertOk)); if (!convertOnly && convertOk) { if (typedValue < rawMin() || typedValue > rawMax()) { - errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt()); + errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt()); + } + } + break; + case FactMetaData::valueTypeInt64: + typedValue = QVariant(rawValue.toLongLong(&convertOk)); + if (!convertOnly && convertOk) { + if (typedValue < rawMin() || typedValue > rawMax()) { + errorString = tr("Value must be within %1 and %2").arg(rawMin().toInt()).arg(rawMax().toInt()); } } break; @@ -304,7 +346,15 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO typedValue = QVariant(rawValue.toUInt(&convertOk)); if (!convertOnly && convertOk) { if (typedValue < rawMin() || typedValue > rawMax()) { - errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt()); + errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt()); + } + } + break; + case FactMetaData::valueTypeUint64: + typedValue = QVariant(rawValue.toULongLong(&convertOk)); + if (!convertOnly && convertOk) { + if (typedValue < rawMin() || typedValue > rawMax()) { + errorString = tr("Value must be within %1 and %2").arg(rawMin().toUInt()).arg(rawMax().toUInt()); } } break; @@ -312,7 +362,7 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO typedValue = QVariant(rawValue.toFloat(&convertOk)); if (!convertOnly && convertOk) { if (typedValue < rawMin() || typedValue > rawMax()) { - errorString = tr("Value must be within %1 and %2").arg(cookedMin().toFloat()).arg(cookedMax().toFloat()); + errorString = tr("Value must be within %1 and %2").arg(rawMin().toFloat()).arg(rawMax().toFloat()); } } break; @@ -321,7 +371,7 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO typedValue = QVariant(rawValue.toDouble(&convertOk)); if (!convertOnly && convertOk) { if (typedValue < rawMin() || typedValue > rawMax()) { - errorString = tr("Value must be within %1 and %2").arg(cookedMin().toDouble()).arg(cookedMax().toDouble()); + errorString = tr("Value must be within %1 and %2").arg(rawMin().toDouble()).arg(rawMax().toDouble()); } } break; @@ -338,11 +388,11 @@ bool FactMetaData::convertAndValidateRaw(const QVariant& rawValue, bool convertO typedValue = QVariant(rawValue.toByteArray()); break; } - + if (!convertOk) { errorString += tr("Invalid number"); } - + return convertOk && errorString.isEmpty(); } @@ -363,6 +413,14 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co } } break; + case FactMetaData::valueTypeInt64: + typedValue = QVariant(cookedValue.toLongLong(&convertOk)); + if (!convertOnly && convertOk) { + if (cookedMin() > typedValue || typedValue > cookedMax()) { + errorString = tr("Value must be within %1 and %2").arg(cookedMin().toInt()).arg(cookedMax().toInt()); + } + } + break; case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: @@ -373,6 +431,14 @@ bool FactMetaData::convertAndValidateCooked(const QVariant& cookedValue, bool co } } break; + case FactMetaData::valueTypeUint64: + typedValue = QVariant(cookedValue.toULongLong(&convertOk)); + if (!convertOnly && convertOk) { + if (cookedMin() > typedValue || typedValue > cookedMax()) { + errorString = tr("Value must be within %1 and %2").arg(cookedMin().toUInt()).arg(cookedMax().toUInt()); + } + } + break; case FactMetaData::valueTypeFloat: typedValue = QVariant(cookedValue.toFloat(&convertOk)); if (!convertOnly && convertOk) { @@ -427,6 +493,16 @@ bool FactMetaData::clampValue(const QVariant& cookedValue, QVariant& typedValue) } } break; + case FactMetaData::valueTypeInt64: + typedValue = QVariant(cookedValue.toLongLong(&convertOk)); + if (convertOk) { + if (cookedMin() > typedValue) { + typedValue = cookedMin(); + } else if(typedValue > cookedMax()) { + typedValue = cookedMax(); + } + } + break; case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: @@ -439,6 +515,16 @@ bool FactMetaData::clampValue(const QVariant& cookedValue, QVariant& typedValue) } } break; + case FactMetaData::valueTypeUint64: + typedValue = QVariant(cookedValue.toULongLong(&convertOk)); + if (convertOk) { + if (cookedMin() > typedValue) { + typedValue = cookedMin(); + } else if(typedValue > cookedMax()) { + typedValue = cookedMax(); + } + } + break; case FactMetaData::valueTypeFloat: typedValue = QVariant(cookedValue.toFloat(&convertOk)); if (convertOk) { @@ -685,6 +771,16 @@ QVariant FactMetaData::_inchesToCentimeters(const QVariant& inches) return QVariant(inches.toDouble() * constants.inchesToCentimeters); } +QVariant FactMetaData::_celsiusToFarenheit(const QVariant& celsius) +{ + return QVariant(celsius.toDouble() * (9.0 / 5.0) + 32); +} + +QVariant FactMetaData::_farenheitToCelsius(const QVariant& farenheit) +{ + return QVariant((farenheit.toDouble() - 32) * (5.0 / 9.0)); +} + void FactMetaData::setRawUnits(const QString& rawUnits) { _rawUnits = rawUnits; @@ -706,6 +802,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << QStringLiteral("Int16") << QStringLiteral("Uint32") << QStringLiteral("Int32") + << QStringLiteral("Uint64") + << QStringLiteral("Int64") << QStringLiteral("Float") << QStringLiteral("Double") << QStringLiteral("String") @@ -719,6 +817,8 @@ FactMetaData::ValueType_t FactMetaData::stringToType(const QString& typeString, << valueTypeInt16 << valueTypeUint32 << valueTypeInt32 + << valueTypeUint64 + << valueTypeInt64 << valueTypeFloat << valueTypeDouble << valueTypeString @@ -753,6 +853,8 @@ size_t FactMetaData::typeToSize(ValueType_t type) case valueTypeFloat: return 4; + case valueTypeUint64: + case valueTypeInt64: case valueTypeDouble: return 8; @@ -765,7 +867,6 @@ size_t FactMetaData::typeToSize(ValueType_t type) } } - /// Set translators according to app settings void FactMetaData::_setAppSettingsTranslators(void) { @@ -773,10 +874,12 @@ void FactMetaData::_setAppSettingsTranslators(void) if (!_enumStrings.count() && (type() == valueTypeDouble || type() == valueTypeFloat)) { for (size_t i=0; irawUnits == _rawUnits.toLower() && - ((pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->speedUnits()->rawValue().toUInt()) || - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt()))) { + if (pAppSettingsTranslation->rawUnits == _rawUnits.toLower() && ( + (pAppSettingsTranslation->unitType == UnitTemperature && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->temperatureUnits()->rawValue().toUInt()) || + (pAppSettingsTranslation->unitType == UnitSpeed && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->speedUnits()->rawValue().toUInt()) || + (pAppSettingsTranslation->unitType == UnitDistance && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt()) || + (pAppSettingsTranslation->unitType == UnitArea && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt()))) + { _cookedUnits = pAppSettingsTranslation->cookedUnits; setTranslators(pAppSettingsTranslation->rawTranslator, pAppSettingsTranslation->cookedTranslator); return; @@ -789,13 +892,11 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsDist { for (size_t i=0; irawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt())) { + if (pAppSettingsTranslation->rawUnits == rawUnits.toLower() && + (pAppSettingsTranslation->unitType == UnitDistance && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->distanceUnits()->rawValue().toUInt())) { return pAppSettingsTranslation; } } - return NULL; } @@ -803,10 +904,8 @@ const FactMetaData::AppSettingsTranslation_s* FactMetaData::_findAppSettingsArea { for (size_t i=0; irawUnits == rawUnits && - (!pAppSettingsTranslation->speed && pAppSettingsTranslation->speedOrDistanceUnits == qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt()) - ) { + if (pAppSettingsTranslation->rawUnits == rawUnits.toLower() && + (pAppSettingsTranslation->unitType == UnitArea && pAppSettingsTranslation->unitOption == qgcApp()->toolbox()->settingsManager()->unitsSettings()->areaUnits()->rawValue().toUInt())) { return pAppSettingsTranslation; } } @@ -972,29 +1071,58 @@ FactMetaData* FactMetaData::createFromJsonObject(const QJsonObject& json, QObjec if (json.contains(_unitsJsonKey)) { metaData->setRawUnits(json[_unitsJsonKey].toString()); } + + QString defaultValueJsonKey; #ifdef __mobile__ if (json.contains(_mobileDefaultValueJsonKey)) { - metaData->setRawDefaultValue(json[_mobileDefaultValueJsonKey].toVariant()); - } else if (json.contains(_defaultValueJsonKey)) { - metaData->setRawDefaultValue(json[_defaultValueJsonKey].toVariant()); - } -#else - if (json.contains(_defaultValueJsonKey)) { - metaData->setRawDefaultValue(json[_defaultValueJsonKey].toVariant()); + defaultValueJsonKey = _mobileDefaultValueJsonKey; } #endif + if (defaultValueJsonKey.isEmpty() && json.contains(_defaultValueJsonKey)) { + defaultValueJsonKey = _defaultValueJsonKey; + } + if (!defaultValueJsonKey.isEmpty()) { + QVariant typedValue; + QString errorString; + QVariant initialValue = json[defaultValueJsonKey].toVariant(); + if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) { + metaData->setRawDefaultValue(typedValue); + } else { + qWarning() << "Invalid default value, name:" << metaData->name() + << " type:" << metaData->type() + << " value:" << initialValue + << " error:" << errorString; + } + } + if (json.contains(_minJsonKey)) { QVariant typedValue; QString errorString; - metaData->convertAndValidateRaw(json[_minJsonKey].toVariant(), true /* convertOnly */, typedValue, errorString); - metaData->setRawMin(typedValue); + QVariant initialValue = json[_minJsonKey].toVariant(); + if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) { + metaData->setRawMin(typedValue); + } else { + qWarning() << "Invalid min value, name:" << metaData->name() + << " type:" << metaData->type() + << " value:" << initialValue + << " error:" << errorString; + } } + if (json.contains(_maxJsonKey)) { QVariant typedValue; QString errorString; - metaData->convertAndValidateRaw(json[_maxJsonKey].toVariant(), true /* convertOnly */, typedValue, errorString); - metaData->setRawMax(typedValue); + QVariant initialValue = json[_maxJsonKey].toVariant(); + if (metaData->convertAndValidateRaw(initialValue, true /* convertOnly */, typedValue, errorString)) { + metaData->setRawMax(typedValue); + } else { + qWarning() << "Invalid max value, name:" << metaData->name() + << " type:" << metaData->type() + << " value:" << initialValue + << " error:" << errorString; + } } + if (json.contains(_hasControlJsonKey)) { metaData->setHasControl(json[_hasControlJsonKey].toBool()); } else { @@ -1078,3 +1206,11 @@ QVariant FactMetaData::cookedMin(void) const return cookedMin; } } + +void FactMetaData::setVolatileValue(bool bValue) +{ + _volatile = bValue; + if (_volatile) { + _readOnly = true; + } +} diff --git a/src/FactSystem/FactMetaData.h b/src/FactSystem/FactMetaData.h index c88ee72409e4ad7ce07678e4c040cbc1d1ae6f58..4b298ca2422b2407b6b9c3b0fc5e814fc48756f8 100644 --- a/src/FactSystem/FactMetaData.h +++ b/src/FactSystem/FactMetaData.h @@ -27,7 +27,7 @@ class FactMetaData : public QObject { Q_OBJECT - + public: typedef enum { valueTypeUint8, @@ -36,6 +36,8 @@ public: valueTypeInt16, valueTypeUint32, valueTypeInt32, + valueTypeUint64, + valueTypeInt64, valueTypeFloat, valueTypeDouble, valueTypeString, @@ -45,7 +47,7 @@ public: } ValueType_t; typedef QVariant (*Translator)(const QVariant& from); - + FactMetaData(QObject* parent = NULL); FactMetaData(ValueType_t type, QObject* parent = NULL); FactMetaData(ValueType_t type, const QString name, QObject* parent = NULL); @@ -76,6 +78,9 @@ public: /// Returns the string for distance units which has configued by user static QString appSettingsAreaUnitsString(void); + static const QString defaultCategory (); + static const QString defaultGroup (); + int decimalPlaces (void) const; QVariant rawDefaultValue (void) const; QVariant cookedDefaultValue (void) const { return _rawTranslator(rawDefaultValue()); } @@ -84,6 +89,7 @@ public: QVariantList bitmaskValues (void) const { return _bitmaskValues; } QStringList enumStrings (void) const { return _enumStrings; } QVariantList enumValues (void) const { return _enumValues; } + QString category (void) const { return _category; } QString group (void) const { return _group; } QString longDescription (void) const { return _longDescription;} QVariant rawMax (void) const { return _rawMax; } @@ -100,6 +106,8 @@ public: bool rebootRequired (void) const { return _rebootRequired; } bool hasControl (void) const { return _hasControl; } bool readOnly (void) const { return _readOnly; } + bool writeOnly (void) const { return _writeOnly; } + bool volatileValue (void) const { return _volatile; } /// Amount to increment value when used in controls such as spin button or slider with detents. /// NaN for no increment available. @@ -118,6 +126,7 @@ public: void setRawDefaultValue (const QVariant& rawDefaultValue); void setBitmaskInfo (const QStringList& strings, const QVariantList& values); void setEnumInfo (const QStringList& strings, const QVariantList& values); + void setCategory (const QString& category) { _category = category; } void setGroup (const QString& group) { _group = group; } void setLongDescription (const QString& longDescription) { _longDescription = longDescription;} void setRawMax (const QVariant& rawMax); @@ -129,6 +138,8 @@ public: void setIncrement (double increment) { _increment = increment; } void setHasControl (bool bValue) { _hasControl = bValue; } void setReadOnly (bool bValue) { _readOnly = bValue; } + void setWriteOnly (bool bValue) { _writeOnly = bValue; } + void setVolatileValue (bool bValue); void setTranslators(Translator rawTranslator, Translator cookedTranslator); @@ -193,15 +204,23 @@ private: static QVariant _normToPercent(const QVariant& normalized); static QVariant _centimetersToInches(const QVariant& centimeters); static QVariant _inchesToCentimeters(const QVariant& inches); + static QVariant _celsiusToFarenheit(const QVariant& celsius); + static QVariant _farenheitToCelsius(const QVariant& farenheit); + + enum UnitTypes { + UnitDistance = 0, + UnitArea, + UnitSpeed, + UnitTemperature + }; struct AppSettingsTranslation_s { - const char* rawUnits; - const char* cookedUnits; - bool speed; - uint32_t speedOrDistanceUnits; - Translator rawTranslator; - Translator cookedTranslator; - + const char* rawUnits; + const char* cookedUnits; + UnitTypes unitType; + uint32_t unitOption; + Translator rawTranslator; + Translator cookedTranslator; }; static const AppSettingsTranslation_s* _findAppSettingsDistanceUnitsTranslation(const QString& rawUnits); @@ -215,6 +234,7 @@ private: QVariantList _bitmaskValues; QStringList _enumStrings; QVariantList _enumValues; + QString _category; QString _group; QString _longDescription; QVariant _rawMax; @@ -231,6 +251,8 @@ private: double _increment; bool _hasControl; bool _readOnly; + bool _writeOnly; + bool _volatile; // Exact conversion constants static const struct UnitConsts_s { diff --git a/src/FactSystem/ParameterManager.cc b/src/FactSystem/ParameterManager.cc index 297f97b8d1c169ecf43cb76707a5185edd187a51..ec1cd138437424c3cd39d5cf922375d3b680bf52 100644 --- a/src/FactSystem/ParameterManager.cc +++ b/src/FactSystem/ParameterManager.cc @@ -7,10 +7,6 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - #include "ParameterManager.h" #include "QGCApplication.h" #include "QGCLoggingCategory.h" @@ -26,13 +22,9 @@ #include #include -/* types for local parameter cache */ -typedef QPair ParamTypeVal; -typedef QPair NamedParam; -typedef QMap MapID2NamedParam; - -QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "ParameterManagerVerbose1Log") -QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "ParameterManagerVerbose2Log") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose1Log, "ParameterManagerVerbose1Log") +QGC_LOGGING_CATEGORY(ParameterManagerVerbose2Log, "ParameterManagerVerbose2Log") +QGC_LOGGING_CATEGORY(ParameterManagerDebugCacheFailureLog, "ParameterManagerDebugCacheFailureLog") // Turn on to debug parameter cache crc misses Fact ParameterManager::_defaultFact; @@ -43,25 +35,26 @@ const char* ParameterManager::_jsonParamNameKey = "name"; const char* ParameterManager::_jsonParamValueKey = "value"; ParameterManager::ParameterManager(Vehicle* vehicle) - : QObject(vehicle) - , _vehicle(vehicle) - , _mavlink(NULL) - , _loadProgress(0.0) - , _parametersReady(false) - , _missingParameters(false) - , _initialLoadComplete(false) - , _waitingForDefaultComponent(false) - , _saveRequired(false) - , _logReplay(vehicle->priorityLink() && vehicle->priorityLink()->isLogReplay()) - , _parameterSetMajorVersion(-1) - , _parameterMetaData(NULL) - , _prevWaitingReadParamIndexCount(0) - , _prevWaitingReadParamNameCount(0) - , _prevWaitingWriteParamNameCount(0) - , _initialRequestRetryCount(0) - , _disableAllRetries(false) - , _indexBatchQueueActive(false) - , _totalParamCount(0) + : QObject (vehicle) + , _vehicle (vehicle) + , _mavlink (NULL) + , _loadProgress (0.0) + , _parametersReady (false) + , _missingParameters (false) + , _initialLoadComplete (false) + , _waitingForDefaultComponent (false) + , _saveRequired (false) + , _metaDataAddedToFacts (false) + , _logReplay (vehicle->priorityLink() && vehicle->priorityLink()->isLogReplay()) + , _parameterSetMajorVersion (-1) + , _parameterMetaData (NULL) + , _prevWaitingReadParamIndexCount (0) + , _prevWaitingReadParamNameCount (0) + , _prevWaitingWriteParamNameCount (0) + , _initialRequestRetryCount (0) + , _disableAllRetries (false) + , _indexBatchQueueActive (false) + , _totalParamCount (0) { _versionParam = vehicle->firmwarePlugin()->getVersionParam(); @@ -85,7 +78,17 @@ ParameterManager::ParameterManager(Vehicle* vehicle) // Ensure the cache directory exists QFileInfo(QSettings().fileName()).dir().mkdir("ParamCache"); - refreshAllParameters(); + if (_vehicle->highLatencyLink()) { + // High latency links don't load parameters + _parametersReady = true; + _missingParameters = true; + _initialLoadComplete = true; + _waitingForDefaultComponent = false; + emit parametersReadyChanged(_parametersReady); + emit missingParametersChanged(_missingParameters); + } else { + refreshAllParameters(); + } } ParameterManager::~ParameterManager() @@ -137,12 +140,32 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString } #endif - if (_vehicle->px4Firmware() && parameterName == "_HASH_CHECK" && !_logReplay) { - /* we received a cache hash, potentially load from cache */ - _tryCacheHashLoad(vehicleId, componentId, value); + if (_vehicle->px4Firmware() && parameterName == "_HASH_CHECK") { + if (!_initialLoadComplete && !_logReplay) { + /* we received a cache hash, potentially load from cache */ + _tryCacheHashLoad(vehicleId, componentId, value); + } return; } + // Used to debug cache crc misses (turn on ParameterManagerDebugCacheFailureLog) + if (!_initialLoadComplete && !_logReplay && _debugCacheCRC.contains(componentId) && _debugCacheCRC[componentId]) { + if (_debugCacheMap[componentId].contains(parameterName)) { + const ParamTypeVal& cacheParamTypeVal = _debugCacheMap[componentId][parameterName]; + size_t dataSize = FactMetaData::typeToSize(static_cast(cacheParamTypeVal.first)); + const void *cacheData = cacheParamTypeVal.second.constData(); + + const void *vehicleData = value.constData(); + + if (memcmp(cacheData, vehicleData, dataSize)) { + qDebug() << "Cache/Vehicle values differ for name:cache:actual" << parameterName << value << cacheParamTypeVal.second; + } + _debugCacheParamSeen[componentId][parameterName] = true; + } else { + qDebug() << "Parameter missing from cache" << parameterName; + } + } + _initialRequestTimeoutTimer.stop(); _waitingParamTimeoutTimer.stop(); @@ -154,8 +177,6 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString _totalParamCount += parameterCount; } - _mapParameterId2Name[componentId][parameterId] = parameterName; - // If we've never seen this component id before, setup the wait lists. if (!_waitingReadParamIndexMap.contains(componentId)) { // Add all indices to the wait list, parameter index is 0-based @@ -178,8 +199,8 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString } if (!_waitingReadParamIndexMap[componentId].contains(parameterId) && - !_waitingReadParamNameMap[componentId].contains(parameterName) && - !_waitingWriteParamNameMap[componentId].contains(parameterName)) { + !_waitingReadParamNameMap[componentId].contains(parameterName) && + !_waitingWriteParamNameMap[componentId].contains(parameterName)) { qCDebug(ParameterManagerVerbose1Log) << _logVehiclePrefix() << "Unrequested param update" << parameterName; } @@ -265,34 +286,40 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString FactMetaData::ValueType_t factType; switch (mavType) { - case MAV_PARAM_TYPE_UINT8: - factType = FactMetaData::valueTypeUint8; - break; - case MAV_PARAM_TYPE_INT8: - factType = FactMetaData::valueTypeInt8; - break; - case MAV_PARAM_TYPE_UINT16: - factType = FactMetaData::valueTypeUint16; - break; - case MAV_PARAM_TYPE_INT16: - factType = FactMetaData::valueTypeInt16; - break; - case MAV_PARAM_TYPE_UINT32: - factType = FactMetaData::valueTypeUint32; - break; - case MAV_PARAM_TYPE_INT32: - factType = FactMetaData::valueTypeInt32; - break; - case MAV_PARAM_TYPE_REAL32: - factType = FactMetaData::valueTypeFloat; - break; - case MAV_PARAM_TYPE_REAL64: - factType = FactMetaData::valueTypeDouble; - break; - default: - factType = FactMetaData::valueTypeInt32; - qCritical() << "Unsupported fact type" << mavType; - break; + case MAV_PARAM_TYPE_UINT8: + factType = FactMetaData::valueTypeUint8; + break; + case MAV_PARAM_TYPE_INT8: + factType = FactMetaData::valueTypeInt8; + break; + case MAV_PARAM_TYPE_UINT16: + factType = FactMetaData::valueTypeUint16; + break; + case MAV_PARAM_TYPE_INT16: + factType = FactMetaData::valueTypeInt16; + break; + case MAV_PARAM_TYPE_UINT32: + factType = FactMetaData::valueTypeUint32; + break; + case MAV_PARAM_TYPE_INT32: + factType = FactMetaData::valueTypeInt32; + break; + case MAV_PARAM_TYPE_UINT64: + factType = FactMetaData::valueTypeUint64; + break; + case MAV_PARAM_TYPE_INT64: + factType = FactMetaData::valueTypeInt64; + break; + case MAV_PARAM_TYPE_REAL32: + factType = FactMetaData::valueTypeFloat; + break; + case MAV_PARAM_TYPE_REAL64: + factType = FactMetaData::valueTypeDouble; + break; + default: + factType = FactMetaData::valueTypeInt32; + qCritical() << "Unsupported fact type" << mavType; + break; } Fact* fact = new Fact(componentId, parameterName, factType, this); @@ -325,12 +352,7 @@ void ParameterManager::_parameterUpdate(int vehicleId, int componentId, QString // When we are getting the very last component param index, reset the group maps to update for the // new params. By handling this here, we can pick up components which finish up later than the default // component param set. - _setupGroupMap(); - } - - if (_prevWaitingWriteParamNameCount != 0 && waitingWriteParamNameCount == 0) { - // If all the writes just finished the vehicle is up to date, so persist. - _saveToEEPROM(); + _setupCategoryMap(); } // Update param cache. The param cache is only used on PX4 Firmware since ArduPilot and Solo have volatile params @@ -511,22 +533,20 @@ QStringList ParameterManager::parameterNames(int componentId) return names; } -void ParameterManager::_setupGroupMap(void) +void ParameterManager::_setupCategoryMap(void) { // Must be able to handle being called multiple times - _mapGroup2ParameterName.clear(); + _categoryMap.clear(); - foreach (int componentId, _mapParameterName2Variant.keys()) { - foreach (const QString &name, _mapParameterName2Variant[componentId].keys()) { - Fact* fact = _mapParameterName2Variant[componentId][name].value(); - _mapGroup2ParameterName[componentId][fact->group()] += name; - } + foreach (const QString &name, _mapParameterName2Variant[_vehicle->defaultComponentId()].keys()) { + Fact* fact = _mapParameterName2Variant[_vehicle->defaultComponentId()][name].value(); + _categoryMap[fact->category()][fact->group()] += name; } } -const QMap >& ParameterManager::getGroupMap(void) +const QMap >& ParameterManager::getCategoryMap(void) { - return _mapGroup2ParameterName; + return _categoryMap; } /// Requests missing index based parameters from the vehicle. @@ -687,37 +707,37 @@ void ParameterManager::_writeParameterRaw(int componentId, const QString& paramN p.param_type = _factTypeToMavType(factType); switch (factType) { - case FactMetaData::valueTypeUint8: - union_value.param_uint8 = (uint8_t)value.toUInt(); - break; + case FactMetaData::valueTypeUint8: + union_value.param_uint8 = (uint8_t)value.toUInt(); + break; - case FactMetaData::valueTypeInt8: - union_value.param_int8 = (int8_t)value.toInt(); - break; + case FactMetaData::valueTypeInt8: + union_value.param_int8 = (int8_t)value.toInt(); + break; - case FactMetaData::valueTypeUint16: - union_value.param_uint16 = (uint16_t)value.toUInt(); - break; + case FactMetaData::valueTypeUint16: + union_value.param_uint16 = (uint16_t)value.toUInt(); + break; - case FactMetaData::valueTypeInt16: - union_value.param_int16 = (int16_t)value.toInt(); - break; + case FactMetaData::valueTypeInt16: + union_value.param_int16 = (int16_t)value.toInt(); + break; - case FactMetaData::valueTypeUint32: - union_value.param_uint32 = (uint32_t)value.toUInt(); - break; + case FactMetaData::valueTypeUint32: + union_value.param_uint32 = (uint32_t)value.toUInt(); + break; - case FactMetaData::valueTypeFloat: - union_value.param_float = value.toFloat(); - break; + case FactMetaData::valueTypeFloat: + union_value.param_float = value.toFloat(); + break; - default: - qCritical() << "Unsupported fact type" << factType; - // fall through + default: + qCritical() << "Unsupported fact type" << factType; + // fall through - case FactMetaData::valueTypeInt32: - union_value.param_int32 = (int32_t)value.toInt(); - break; + case FactMetaData::valueTypeInt32: + union_value.param_int32 = (int32_t)value.toInt(); + break; } p.param_value = union_value.param_float; @@ -737,19 +757,18 @@ void ParameterManager::_writeParameterRaw(int componentId, const QString& paramN void ParameterManager::_writeLocalParamCache(int vehicleId, int componentId) { - MapID2NamedParam cache_map; + CacheMapName2ParamTypeVal cacheMap; - foreach(int id, _mapParameterId2Name[componentId].keys()) { - const QString name(_mapParameterId2Name[componentId][id]); + foreach(const QString& name, _mapParameterName2Variant[componentId].keys()) { const Fact *fact = _mapParameterName2Variant[componentId][name].value(); - cache_map[id] = NamedParam(name, ParamTypeVal(fact->type(), fact->rawValue())); + cacheMap[name] = ParamTypeVal(fact->type(), fact->rawValue()); } - QFile cache_file(parameterCacheFile(vehicleId, componentId)); - cache_file.open(QIODevice::WriteOnly | QIODevice::Truncate); + QFile cacheFile(parameterCacheFile(vehicleId, componentId)); + cacheFile.open(QIODevice::WriteOnly | QIODevice::Truncate); - QDataStream ds(&cache_file); - ds << cache_map; + QDataStream ds(&cacheFile); + ds << cacheMap; } QDir ParameterManager::parameterCacheDir() @@ -760,46 +779,70 @@ QDir ParameterManager::parameterCacheDir() QString ParameterManager::parameterCacheFile(int vehicleId, int componentId) { - return parameterCacheDir().filePath(QString("%1_%2").arg(vehicleId).arg(componentId)); + return parameterCacheDir().filePath(QString("%1_%2.v2").arg(vehicleId).arg(componentId)); } void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, QVariant hash_value) { + qCInfo(ParameterManagerLog) << "Attemping load from cache"; + uint32_t crc32_value = 0; /* The datastructure of the cache table */ - MapID2NamedParam cache_map; - QFile cache_file(parameterCacheFile(vehicleId, componentId)); - if (!cache_file.exists()) { + CacheMapName2ParamTypeVal cacheMap; + QFile cacheFile(parameterCacheFile(vehicleId, componentId)); + if (!cacheFile.exists()) { /* no local cache, just wait for them to come in*/ return; } - cache_file.open(QIODevice::ReadOnly); + cacheFile.open(QIODevice::ReadOnly); /* Deserialize the parameter cache table */ - QDataStream ds(&cache_file); - ds >> cache_map; + QDataStream ds(&cacheFile); + ds >> cacheMap; + + // Load parameter meta data for the version number stored in cache. + // We need meta data so we have access to the volatile bit + if (cacheMap.contains(_versionParam)) { + _parameterSetMajorVersion = cacheMap[_versionParam].second.toInt(); + } + _loadMetaData(); /* compute the crc of the local cache to check against the remote */ - foreach(int id, cache_map.keys()) { - const QString name(cache_map[id].first); - const void *vdat = cache_map[id].second.second.constData(); - const FactMetaData::ValueType_t fact_type = static_cast(cache_map[id].second.first); - crc32_value = QGC::crc32((const uint8_t *)qPrintable(name), name.length(), crc32_value); - crc32_value = QGC::crc32((const uint8_t *)vdat, FactMetaData::typeToSize(fact_type), crc32_value); + FirmwarePlugin* firmwarePlugin = _vehicle->firmwarePlugin(); + foreach (const QString& name, cacheMap.keys()) { + bool volatileValue = false; + + FactMetaData* metaData = firmwarePlugin->getMetaDataForFact(_parameterMetaData, name, _vehicle->vehicleType()); + if (metaData) { + volatileValue = metaData->volatileValue(); + } + + if (volatileValue) { + // Does not take part in CRC + qCDebug(ParameterManagerLog) << "Volatile parameter" << name; + } else { + const ParamTypeVal& paramTypeVal = cacheMap[name]; + const void *vdat = paramTypeVal.second.constData(); + const FactMetaData::ValueType_t fact_type = static_cast(paramTypeVal.first); + crc32_value = QGC::crc32((const uint8_t *)qPrintable(name), name.length(), crc32_value); + crc32_value = QGC::crc32((const uint8_t *)vdat, FactMetaData::typeToSize(fact_type), crc32_value); + } } + /* if the two param set hashes match, just load from the disk */ if (crc32_value == hash_value.toUInt()) { - qCInfo(ParameterManagerLog) << "Parameters loaded from cache" << qPrintable(QFileInfo(cache_file).absoluteFilePath()); - /* if the two param set hashes match, just load from the disk */ - int count = cache_map.count(); - foreach(int id, cache_map.keys()) { - const QString &name = cache_map[id].first; - const QVariant &value = cache_map[id].second.second; - const FactMetaData::ValueType_t fact_type = static_cast(cache_map[id].second.first); + qCInfo(ParameterManagerLog) << "Parameters loaded from cache" << qPrintable(QFileInfo(cacheFile).absoluteFilePath()); + + int count = cacheMap.count(); + int index = 0; + foreach (const QString& name, cacheMap.keys()) { + const ParamTypeVal& paramTypeVal = cacheMap[name]; + const FactMetaData::ValueType_t fact_type = static_cast(paramTypeVal.first); const int mavType = _factTypeToMavType(fact_type); - _parameterUpdate(vehicleId, componentId, name, count, id, mavType, value); + _parameterUpdate(vehicleId, componentId, name, count, index++, mavType, paramTypeVal.second); } + // Return the hash value to notify we don't want any more updates mavlink_param_set_t p; mavlink_param_union_t union_value; @@ -837,22 +880,18 @@ void ParameterManager::_tryCacheHashLoad(int vehicleId, int componentId, QVarian }); ani->start(QAbstractAnimation::DeleteWhenStopped); - } -} - -void ParameterManager::_saveToEEPROM(void) -{ - if (_saveRequired) { - _saveRequired = false; - if (_vehicle->firmwarePlugin()->isCapable(_vehicle, FirmwarePlugin::MavCmdPreflightStorageCapability)) { - _vehicle->sendMavCommand(MAV_COMP_ID_ALL, - MAV_CMD_PREFLIGHT_STORAGE, - true, // showError - 1, // Write parameters to EEPROM - -1); // Don't do anything with mission storage - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM"; - } else { - qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "_saveToEEPROM skipped due to FirmwarePlugin::isCapable"; + } else { + // Cache parameter version may differ from vehicle parameter version so we can't trust information loaded from cache parameter version number + _parameterSetMajorVersion = -1; + _clearMetaData(); + qCInfo(ParameterManagerLog) << "Parameters cache match failed" << qPrintable(QFileInfo(cacheFile).absoluteFilePath()); + if (ParameterManagerDebugCacheFailureLog().isDebugEnabled()) { + _debugCacheCRC[componentId] = true; + _debugCacheMap[componentId] = cacheMap; + foreach (const QString& name, cacheMap.keys()) { + _debugCacheParamSeen[componentId][name] = false; + } + qgcApp()->showMessage(tr("Parameter cache CRC match failed")); } } } @@ -936,77 +975,112 @@ void ParameterManager::writeParametersToStream(QTextStream &stream) MAV_PARAM_TYPE ParameterManager::_factTypeToMavType(FactMetaData::ValueType_t factType) { switch (factType) { - case FactMetaData::valueTypeUint8: - return MAV_PARAM_TYPE_UINT8; + case FactMetaData::valueTypeUint8: + return MAV_PARAM_TYPE_UINT8; - case FactMetaData::valueTypeInt8: - return MAV_PARAM_TYPE_INT8; + case FactMetaData::valueTypeInt8: + return MAV_PARAM_TYPE_INT8; - case FactMetaData::valueTypeUint16: - return MAV_PARAM_TYPE_UINT16; + case FactMetaData::valueTypeUint16: + return MAV_PARAM_TYPE_UINT16; - case FactMetaData::valueTypeInt16: - return MAV_PARAM_TYPE_INT16; + case FactMetaData::valueTypeInt16: + return MAV_PARAM_TYPE_INT16; - case FactMetaData::valueTypeUint32: - return MAV_PARAM_TYPE_UINT32; + case FactMetaData::valueTypeUint32: + return MAV_PARAM_TYPE_UINT32; - case FactMetaData::valueTypeFloat: - return MAV_PARAM_TYPE_REAL32; + case FactMetaData::valueTypeUint64: + return MAV_PARAM_TYPE_UINT64; - default: - qWarning() << "Unsupported fact type" << factType; - // fall through + case FactMetaData::valueTypeInt64: + return MAV_PARAM_TYPE_INT64; + + case FactMetaData::valueTypeFloat: + return MAV_PARAM_TYPE_REAL32; - case FactMetaData::valueTypeInt32: - return MAV_PARAM_TYPE_INT32; + case FactMetaData::valueTypeDouble: + return MAV_PARAM_TYPE_REAL64; + + default: + qWarning() << "Unsupported fact type" << factType; + // fall through + + case FactMetaData::valueTypeInt32: + return MAV_PARAM_TYPE_INT32; } } FactMetaData::ValueType_t ParameterManager::_mavTypeToFactType(MAV_PARAM_TYPE mavType) { switch (mavType) { - case MAV_PARAM_TYPE_UINT8: - return FactMetaData::valueTypeUint8; + case MAV_PARAM_TYPE_UINT8: + return FactMetaData::valueTypeUint8; - case MAV_PARAM_TYPE_INT8: - return FactMetaData::valueTypeInt8; + case MAV_PARAM_TYPE_INT8: + return FactMetaData::valueTypeInt8; - case MAV_PARAM_TYPE_UINT16: - return FactMetaData::valueTypeUint16; + case MAV_PARAM_TYPE_UINT16: + return FactMetaData::valueTypeUint16; - case MAV_PARAM_TYPE_INT16: - return FactMetaData::valueTypeInt16; + case MAV_PARAM_TYPE_INT16: + return FactMetaData::valueTypeInt16; - case MAV_PARAM_TYPE_UINT32: - return FactMetaData::valueTypeUint32; + case MAV_PARAM_TYPE_UINT32: + return FactMetaData::valueTypeUint32; - case MAV_PARAM_TYPE_REAL32: - return FactMetaData::valueTypeFloat; + case MAV_PARAM_TYPE_UINT64: + return FactMetaData::valueTypeUint64; - default: - qWarning() << "Unsupported mav param type" << mavType; - // fall through + case MAV_PARAM_TYPE_INT64: + return FactMetaData::valueTypeInt64; - case MAV_PARAM_TYPE_INT32: - return FactMetaData::valueTypeInt32; + case MAV_PARAM_TYPE_REAL32: + return FactMetaData::valueTypeFloat; + + case MAV_PARAM_TYPE_REAL64: + return FactMetaData::valueTypeDouble; + + default: + qWarning() << "Unsupported mav param type" << mavType; + // fall through + + case MAV_PARAM_TYPE_INT32: + return FactMetaData::valueTypeInt32; } } -void ParameterManager::_addMetaDataToDefaultComponent(void) +void ParameterManager::_clearMetaData(void) +{ + if (_parameterMetaData) { + _parameterMetaData->deleteLater(); + _parameterMetaData = NULL; + } +} + +void ParameterManager::_loadMetaData(void) { - if (_parameterMetaData) { - return; - } + if (_parameterMetaData) { + return; + } - QString metaDataFile; - int majorVersion, minorVersion; + QString metaDataFile; + int majorVersion, minorVersion; - // Load best parameter meta data set - metaDataFile = parameterMetaDataFile(_vehicle, _vehicle->firmwareType(), _parameterSetMajorVersion, majorVersion, minorVersion); - qCDebug(ParameterManagerLog) << "Adding meta data to Vehicle file:major:minor" << metaDataFile << majorVersion << minorVersion; + // Load best parameter meta data set + metaDataFile = parameterMetaDataFile(_vehicle, _vehicle->firmwareType(), _parameterSetMajorVersion, majorVersion, minorVersion); + qCDebug(ParameterManagerLog) << "Loading meta data file:major:minor" << metaDataFile << majorVersion << minorVersion; + _parameterMetaData = _vehicle->firmwarePlugin()->loadParameterMetaData(metaDataFile); +} - _parameterMetaData = _vehicle->firmwarePlugin()->loadParameterMetaData(metaDataFile); +void ParameterManager::_addMetaDataToDefaultComponent(void) +{ + _loadMetaData(); + + if (_metaDataAddedToFacts) { + return; + } + _metaDataAddedToFacts = true; // Loop over all parameters in default component adding meta data QVariantMap& factMap = _mapParameterName2Variant[_vehicle->defaultComponentId()]; @@ -1037,6 +1111,18 @@ void ParameterManager::_checkInitialLoadComplete(void) // We aren't waiting for any more initial parameter updates, initial parameter loading is complete _initialLoadComplete = true; + // Parameter cache crc failure debugging + foreach (int componentId, _debugCacheParamSeen.keys()) { + if (!_logReplay && _debugCacheCRC.contains(componentId) && _debugCacheCRC[componentId]) { + foreach (const QString& paramName, _debugCacheParamSeen[componentId].keys()) { + if (!_debugCacheParamSeen[componentId][paramName]) { + qDebug() << "Parameter in cache but not on vehicle componentId:Name" << componentId << paramName; + } + } + } + } + _debugCacheCRC.clear(); + qCDebug(ParameterManagerLog) << _logVehiclePrefix() << "Initial load complete"; // Check for index based load failures @@ -1347,9 +1433,10 @@ void ParameterManager::_loadOfflineEditingParams(void) } _addMetaDataToDefaultComponent(); - _setupGroupMap(); + _setupCategoryMap(); _parametersReady = true; _initialLoadComplete = true; + _debugCacheCRC.clear(); } void ParameterManager::saveToJson(int componentId, const QStringList& paramsToSave, QJsonObject& saveObject) diff --git a/src/FactSystem/ParameterManager.h b/src/FactSystem/ParameterManager.h index 7714075d7ac6605b0e73f6ae0a56f7dc45c2213c..33d14ff8a3aa64a8fcd868f5a327bcfd44089227 100644 --- a/src/FactSystem/ParameterManager.h +++ b/src/FactSystem/ParameterManager.h @@ -30,6 +30,7 @@ Q_DECLARE_LOGGING_CATEGORY(ParameterManagerVerbose1Log) Q_DECLARE_LOGGING_CATEGORY(ParameterManagerVerbose2Log) +Q_DECLARE_LOGGING_CATEGORY(ParameterManagerDebugCacheFailureLog) /// Connects to Parameter Manager to load/update Facts class ParameterManager : public QObject @@ -84,7 +85,7 @@ public: /// @param name Parameter name Fact* getParameter(int componentId, const QString& name); - const QMap >& getGroupMap(void); + const QMap >& getCategoryMap(void); /// Returns error messages from loading QString readParametersFromStream(QTextStream& stream); @@ -137,11 +138,13 @@ protected: private: static QVariant _stringToTypedVariant(const QString& string, FactMetaData::ValueType_t type, bool failOk = false); int _actualComponentId(int componentId); - void _setupGroupMap(void); + void _setupCategoryMap(void); void _readParameterRaw(int componentId, const QString& paramName, int paramIndex); void _writeParameterRaw(int componentId, const QString& paramName, const QVariant& value); void _writeLocalParamCache(int vehicleId, int componentId); void _tryCacheHashLoad(int vehicleId, int componentId, QVariant hash_value); + void _loadMetaData(void); + void _clearMetaData(void); void _addMetaDataToDefaultComponent(void); QString _remapParamNameToVersion(const QString& paramName); void _loadOfflineEditingParams(void); @@ -151,18 +154,14 @@ private: MAV_PARAM_TYPE _factTypeToMavType(FactMetaData::ValueType_t factType); FactMetaData::ValueType_t _mavTypeToFactType(MAV_PARAM_TYPE mavType); - void _saveToEEPROM(void); void _checkInitialLoadComplete(void); /// First mapping is by component id /// Second mapping is parameter name, to Fact* in QVariant QMap _mapParameterName2Variant; - QMap > _mapParameterId2Name; - - /// First mapping is by component id - /// Second mapping is group name, to Fact - QMap > _mapGroup2ParameterName; + // Category map of default component parameters + QMap > _categoryMap; double _loadProgress; ///< Parameter load progess, [0.0,1.0] bool _parametersReady; ///< true: parameter load complete @@ -170,11 +169,19 @@ private: bool _initialLoadComplete; ///< true: Initial load of all parameters complete, whether successful or not bool _waitingForDefaultComponent; ///< true: last chance wait for default component params bool _saveRequired; ///< true: _saveToEEPROM should be called + bool _metaDataAddedToFacts; ///< true: FactMetaData has been adde to the default component facts bool _logReplay; ///< true: running with log replay link QString _versionParam; ///< Parameter which contains parameter set version int _parameterSetMajorVersion; ///< Version for parameter set, -1 if not known QObject* _parameterMetaData; ///< Opaque data from FirmwarePlugin::loadParameterMetaDataCall + typedef QPair ParamTypeVal; + typedef QMap CacheMapName2ParamTypeVal; + + QMap _debugCacheCRC; ///< true: debug cache crc failure + QMap _debugCacheMap; + QMap> _debugCacheParamSeen; + // Wait counts from previous parameter update cycle int _prevWaitingReadParamIndexCount; int _prevWaitingReadParamNameCount; diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc index c2f353342a8d2065545683b35bf1dd235dd1d9b3..744114b391ca39ead17d32cbb16b8e2db0aec8fe 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.cc @@ -911,6 +911,23 @@ void APMFirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) _guidedModeTakeoff(vehicle, altitudeRel); } +double APMFirmwarePlugin::minimumTakeoffAltitude(Vehicle* vehicle) +{ + double minTakeoffAlt = 0; + QString takeoffAltParam(vehicle->vtol() ? QStringLiteral("Q_RTL_ALT") : QStringLiteral("PILOT_TKOFF_ALT")); + float paramDivisor = vehicle->vtol() ? 1.0 : 100.0; // PILOT_TAKEOFF_ALT is in centimeters + + if (vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, takeoffAltParam)) { + minTakeoffAlt = vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, takeoffAltParam)->rawValue().toDouble() / paramDivisor; + } + + if (minTakeoffAlt == 0) { + minTakeoffAlt = FirmwarePlugin::minimumTakeoffAltitude(vehicle); + } + + return minTakeoffAlt; +} + bool APMFirmwarePlugin::_guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) { if (!vehicle->multiRotor() && !vehicle->vtol()) { @@ -924,20 +941,7 @@ bool APMFirmwarePlugin::_guidedModeTakeoff(Vehicle* vehicle, double altitudeRel) return false; } - QString takeoffAltParam(vehicle->vtol() ? QStringLiteral("Q_RTL_ALT") : QStringLiteral("PILOT_TKOFF_ALT")); - float paramDivisor = vehicle->vtol() ? 1.0 : 100.0; // PILOT_TAKEOFF_ALT is in centimeters - - float takeoffAltRel = 0; - if (vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, takeoffAltParam)) { - Fact* takeoffAltFact = vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, takeoffAltParam); - takeoffAltRel = takeoffAltFact->rawValue().toDouble(); - } - if (takeoffAltRel <= 0) { - takeoffAltRel = 2.5; - } else { - takeoffAltRel /= paramDivisor; // centimeters -> meters - } - + double takeoffAltRel = minimumTakeoffAltitude(vehicle); if (!qIsNaN(altitudeRel) && altitudeRel > takeoffAltRel) { takeoffAltRel = altitudeRel; } diff --git a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h index 41aeb24ef9cf8c75e9a167b4ea0ea22a1adc906d..1353e0ca087947550cbf0b5a33bb0920bc312bd7 100644 --- a/src/FirmwarePlugin/APM/APMFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/APMFirmwarePlugin.h @@ -78,6 +78,7 @@ public: void setGuidedMode (Vehicle* vehicle, bool guidedMode) override; void guidedModeTakeoff (Vehicle* vehicle, double altitudeRel) override; void guidedModeGotoLocation (Vehicle* vehicle, const QGeoCoordinate& gotoCoord) override; + double minimumTakeoffAltitude (Vehicle* vehicle) override; void startMission (Vehicle* vehicle) override; QStringList flightModes (Vehicle* vehicle) override; QString flightMode (uint8_t base_mode, uint32_t custom_mode) const override; diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml index 37e9b0d2145df639e548676cbadd28f198b10629..e3b70027b97af1c87b7b49b5f4327cf85d6aff19 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Copter.3.5.xml @@ -1336,7 +1336,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1359,7 +1359,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1382,7 +1382,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1405,7 +1405,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1428,7 +1428,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1451,7 +1451,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml index 9e31d7f2e22aa4ad706442630466665df25e25a1..9a849d2036cb888981ddb3f84bef82b3c87cac3c 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.7.xml @@ -1094,7 +1094,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1117,7 +1117,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1140,7 +1140,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1163,7 +1163,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1186,7 +1186,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1209,7 +1209,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml index c81723d104cc46d9874461879aeffe6a906d9bd9..a072cb23bd537e304767f855492b3932e7e16494 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Plane.3.8.xml @@ -1013,7 +1013,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1036,7 +1036,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1059,7 +1059,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1082,7 +1082,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1105,7 +1105,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1128,7 +1128,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL diff --git a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml index 763d5341ff4e891d1a000ee97cdb2ea72ad1f187..5b725046df3777703c6ead9c10cfd77e2e581dfc 100644 --- a/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml +++ b/src/FirmwarePlugin/APM/APMParameterFactMetaData.Rover.3.2.xml @@ -1013,7 +1013,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1036,7 +1036,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1059,7 +1059,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1082,7 +1082,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1105,7 +1105,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL @@ -1128,7 +1128,7 @@ Throw Avoid_ADSB Guided_NoGPS -SafeRTL +SmartRTL diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.cc b/src/FirmwarePlugin/APM/APMParameterMetaData.cc index f5154f921b16e52488b436624990fedb46e38195..2760464324a7ea528ca20a9e2e31818b98dc8868 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.cc +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.cc @@ -44,11 +44,13 @@ QVariant APMParameterMetaData::_stringToTypedVariant(const QString& string, case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: + case FactMetaData::valueTypeUint64: convertTo = QVariant::UInt; break; case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt16: case FactMetaData::valueTypeInt32: + case FactMetaData::valueTypeInt64: convertTo = QVariant::Int; break; case FactMetaData::valueTypeFloat: @@ -233,14 +235,18 @@ void APMParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData QString group = name.split('_').first(); group = group.remove(QRegExp("[0-9]*$")); // remove any numbers from the end + QString category = xml.attributes().value("user").toString(); + if (category.isEmpty()) { + category = QStringLiteral("Advanced"); + } + QString shortDescription = xml.attributes().value("humanName").toString(); QString longDescription = xml.attributes().value("documentation").toString(); - QString userLevel = xml.attributes().value("user").toString(); qCDebug(APMParameterMetaDataVerboseLog) << "Found parameter name:" << name << "short Desc:" << shortDescription << "longDescription:" << longDescription - << "user level: " << userLevel + << "category: " << category << "group: " << group; Q_ASSERT(!rawMetaData); @@ -254,6 +260,7 @@ void APMParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData } qCDebug(APMParameterMetaDataVerboseLog) << "inserting metadata for field" << name; rawMetaData->name = name; + rawMetaData->category = category; rawMetaData->group = group; rawMetaData->shortDescription = shortDescription; rawMetaData->longDescription = longDescription; @@ -301,7 +308,7 @@ void APMParameterMetaData::correctGroupMemberships(ParameterNametoFactMetaDataMa foreach(const QString& groupName, groupMembers.keys()) { if (groupMembers[groupName].count() == 1) { foreach(const QString& parameter, groupMembers.value(groupName)) { - parameterToFactMetaDataMap[parameter]->group = "others"; + parameterToFactMetaDataMap[parameter]->group = FactMetaData::defaultGroup(); } } } @@ -434,6 +441,7 @@ void APMParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType) } metaData->setName(rawMetaData->name); + metaData->setCategory(rawMetaData->category); metaData->setGroup(rawMetaData->group); metaData->setRebootRequired(rawMetaData->rebootRequired); @@ -525,12 +533,14 @@ void APMParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType) break; case FactMetaData::valueTypeInt32: + case FactMetaData::valueTypeInt64: typedBitSet = QVariant((int)bitSet); break; case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: + case FactMetaData::valueTypeUint64: typedBitSet = QVariant(bitSet); break; diff --git a/src/FirmwarePlugin/APM/APMParameterMetaData.h b/src/FirmwarePlugin/APM/APMParameterMetaData.h index dc4c4600d6daecfbd65c89a154145487005f4bfb..675476ed2770f743da7187da9c097380a4cf9f76 100644 --- a/src/FirmwarePlugin/APM/APMParameterMetaData.h +++ b/src/FirmwarePlugin/APM/APMParameterMetaData.h @@ -32,6 +32,7 @@ public: { } QString name; + QString category; QString group; QString shortDescription; QString longDescription; diff --git a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc index 58ff75035fa5bd9d2e53241c00c853ae7a9bc052..3df92033b4f289b59f34ecd6b74108ba5513c845 100644 --- a/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduCopterFirmwarePlugin.cc @@ -41,7 +41,7 @@ APMCopterMode::APMCopterMode(uint32_t mode, bool settable) : enumToString.insert(THROW, "Throw"); enumToString.insert(AVOID_ADSB,"Avoid ADSB"); enumToString.insert(GUIDED_NOGPS,"Guided No GPS"); - enumToString.insert(SAFE_RTL,"Safe RTL"); + enumToString.insert(SAFE_RTL,"Smart RTL"); setEnumToStringMapping(enumToString); @@ -145,6 +145,9 @@ ArduCopterFirmwarePlugin::ArduCopterFirmwarePlugin(void) remapV3_5["SERVO13_REVERSED"] = QStringLiteral("RC13_REVERSED"); remapV3_5["SERVO14_REVERSED"] = QStringLiteral("RC14_REVERSED"); + remapV3_5["ARMING_VOLT_MIN"] = QStringLiteral("ARMING_MIN_VOLT"); + remapV3_5["ARMING_VOLT2_MIN"] = QStringLiteral("ARMING_MIN_VOLT2"); + _remapParamNameIntialized = true; } } diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc index 40ec05dafbdfaecfd802ad19e2e2ff91a33f51b2..73f2caf3c2f1b7ca1ab808022b5c95272a0aa0cd 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.cc @@ -110,6 +110,9 @@ ArduPlaneFirmwarePlugin::ArduPlaneFirmwarePlugin(void) remapV3_8["SERVO13_REVERSED"] = QStringLiteral("RC13_REVERSED"); remapV3_8["SERVO14_REVERSED"] = QStringLiteral("RC14_REVERSED"); + remapV3_8["ARMING_VOLT_MIN"] = QStringLiteral("ARMING_MIN_VOLT"); + remapV3_8["ARMING_VOLT2_MIN"] = QStringLiteral("ARMING_MIN_VOLT2"); + _remapParamNameIntialized = true; } } diff --git a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h index e4465c01a1fcef636aec21d824eb81c33e30c13b..cd0ce2e8559353ac394e314f307f8fd535fc47e5 100644 --- a/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduPlaneFirmwarePlugin.h @@ -59,7 +59,8 @@ public: QString pauseFlightMode (void) const override { return QString("Loiter"); } QString offlineEditingParamFile (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/APM/Plane.OfflineEditing.params"); } QString autoDisarmParameter (Vehicle* vehicle) final { Q_UNUSED(vehicle); return QStringLiteral("LAND_DISARMDELAY"); } - int remapParamNameHigestMinorVersionNumber (int majorVersionNumber) const final; + int remapParamNameHigestMinorVersionNumber (int majorVersionNumber) const final; + const FirmwarePlugin::remapParamNameMajorVersionMap_t& paramNameRemapMajorVersionMap(void) const final { return _remapParamName; } private: static bool _remapParamNameIntialized; diff --git a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc index 12459a699b3bb668db7aaccde70e6204f910ddb8..ea8f388546f0048c40dc8ff30c659498e2c10d45 100644 --- a/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduRoverFirmwarePlugin.cc @@ -89,6 +89,9 @@ ArduRoverFirmwarePlugin::ArduRoverFirmwarePlugin(void) remapV3_2["SERVO13_REVERSED"] = QStringLiteral("RC13_REVERSED"); remapV3_2["SERVO14_REVERSED"] = QStringLiteral("RC14_REVERSED"); + remapV3_2["ARMING_VOLT_MIN"] = QStringLiteral("ARMING_MIN_VOLT"); + remapV3_2["ARMING_VOLT2_MIN"] = QStringLiteral("ARMING_MIN_VOLT2"); + _remapParamNameIntialized = true; } } diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc index b06b53990a2157a8d29fee02c76589ddf0be3758..834ce5977d89c7084849ee37744891e15332489f 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.cc @@ -110,16 +110,53 @@ ArduSubFirmwarePlugin::ArduSubFirmwarePlugin(void): remapV3_5["FENCE_ALT_MIN"] = QStringLiteral("FENCE_DEPTH_MAX"); + FirmwarePlugin::remapParamNameMap_t& remapV3_6 = _remapParamName[3][6]; + + remapV3_6["ARMING_VOLT_MIN"] = QStringLiteral("ARMING_MIN_VOLT"); + remapV3_6["ARMING_VOLT2_MIN"] = QStringLiteral("ARMING_MIN_VOLT2"); + _remapParamNameIntialized = true; } _nameToFactGroupMap.insert("APMSubInfo", &_infoFactGroup); } +QList ArduSubFirmwarePlugin::supportedMissionCommands(void) +{ + QList list; + + list << MAV_CMD_NAV_WAYPOINT + << MAV_CMD_NAV_RETURN_TO_LAUNCH + << MAV_CMD_NAV_LAND + << MAV_CMD_NAV_CONTINUE_AND_CHANGE_ALT + << MAV_CMD_NAV_SPLINE_WAYPOINT + << MAV_CMD_NAV_GUIDED_ENABLE + << MAV_CMD_NAV_DELAY + << MAV_CMD_CONDITION_DELAY << MAV_CMD_CONDITION_DISTANCE << MAV_CMD_CONDITION_YAW + << MAV_CMD_DO_SET_MODE + << MAV_CMD_DO_JUMP + << MAV_CMD_DO_CHANGE_SPEED + << MAV_CMD_DO_SET_HOME + << MAV_CMD_DO_SET_RELAY << MAV_CMD_DO_REPEAT_RELAY + << MAV_CMD_DO_SET_SERVO << MAV_CMD_DO_REPEAT_SERVO + << MAV_CMD_DO_LAND_START + << MAV_CMD_DO_SET_ROI + << MAV_CMD_DO_DIGICAM_CONFIGURE << MAV_CMD_DO_DIGICAM_CONTROL + << MAV_CMD_DO_MOUNT_CONTROL + << MAV_CMD_DO_SET_CAM_TRIGG_DIST + << MAV_CMD_DO_FENCE_ENABLE + << MAV_CMD_DO_INVERTED_FLIGHT + << MAV_CMD_DO_GRIPPER + << MAV_CMD_DO_GUIDED_LIMITS + << MAV_CMD_DO_AUTOTUNE_ENABLE; + + return list; +} + int ArduSubFirmwarePlugin::remapParamNameHigestMinorVersionNumber(int majorVersionNumber) const { - // Remapping supports up to 3.5 - return majorVersionNumber == 3 ? 5 : Vehicle::versionNotSetValue; + // Remapping supports up to 3.6 + return majorVersionNumber == 3 ? 6 : Vehicle::versionNotSetValue; } int ArduSubFirmwarePlugin::manualControlReservedButtonCount(void) @@ -178,6 +215,8 @@ void ArduSubFirmwarePlugin::_handleNamedValueFloat(mavlink_message_t* message) _infoFactGroup.getFact("lights 2")->setRawValue(value.value * 100); } else if (name == "PilotGain") { _infoFactGroup.getFact("pilot gain")->setRawValue(value.value * 100); + } else if (name == "InputHold") { + _infoFactGroup.getFact("input hold")->setRawValue(value.value); } } @@ -186,6 +225,14 @@ void ArduSubFirmwarePlugin::_handleMavlinkMessage(mavlink_message_t* message) switch (message->msgid) { case (MAVLINK_MSG_ID_NAMED_VALUE_FLOAT): _handleNamedValueFloat(message); + break; + case (MAVLINK_MSG_ID_RANGEFINDER): + { + mavlink_rangefinder_t msg; + mavlink_msg_rangefinder_decode(message, &msg); + _infoFactGroup.getFact("rangefinder distance")->setRawValue(msg.distance); + break; + } } } @@ -199,32 +246,41 @@ QMap* ArduSubFirmwarePlugin::factGroups(void) { return &_nameToFactGroupMap; } -const char* APMSubmarineFactGroup::_camTiltFactName = "camera tilt"; -const char* APMSubmarineFactGroup::_tetherTurnsFactName = "tether turns"; -const char* APMSubmarineFactGroup::_lightsLevel1FactName = "lights 1"; -const char* APMSubmarineFactGroup::_lightsLevel2FactName = "lights 2"; -const char* APMSubmarineFactGroup::_pilotGainFactName = "pilot gain"; +const char* APMSubmarineFactGroup::_camTiltFactName = "camera tilt"; +const char* APMSubmarineFactGroup::_tetherTurnsFactName = "tether turns"; +const char* APMSubmarineFactGroup::_lightsLevel1FactName = "lights 1"; +const char* APMSubmarineFactGroup::_lightsLevel2FactName = "lights 2"; +const char* APMSubmarineFactGroup::_pilotGainFactName = "pilot gain"; +const char* APMSubmarineFactGroup::_inputHoldFactName = "input hold"; +const char* APMSubmarineFactGroup::_rangefinderDistanceFactName = "rangefinder distance"; APMSubmarineFactGroup::APMSubmarineFactGroup(QObject* parent) : FactGroup(300, ":/json/Vehicle/SubmarineFact.json", parent) - , _camTiltFact (0, _camTiltFactName, FactMetaData::valueTypeDouble) - , _tetherTurnsFact (0, _tetherTurnsFactName, FactMetaData::valueTypeDouble) - , _lightsLevel1Fact (0, _lightsLevel1FactName, FactMetaData::valueTypeDouble) - , _lightsLevel2Fact (0, _lightsLevel2FactName, FactMetaData::valueTypeDouble) - , _pilotGainFact (0, _pilotGainFactName, FactMetaData::valueTypeDouble) -{ - _addFact(&_camTiltFact, _camTiltFactName); - _addFact(&_tetherTurnsFact, _tetherTurnsFactName); - _addFact(&_lightsLevel1Fact, _lightsLevel1FactName); - _addFact(&_lightsLevel2Fact, _lightsLevel2FactName); - _addFact(&_pilotGainFact, _pilotGainFactName); + , _camTiltFact (0, _camTiltFactName, FactMetaData::valueTypeDouble) + , _tetherTurnsFact (0, _tetherTurnsFactName, FactMetaData::valueTypeDouble) + , _lightsLevel1Fact (0, _lightsLevel1FactName, FactMetaData::valueTypeDouble) + , _lightsLevel2Fact (0, _lightsLevel2FactName, FactMetaData::valueTypeDouble) + , _pilotGainFact (0, _pilotGainFactName, FactMetaData::valueTypeDouble) + , _inputHoldFact (0, _inputHoldFactName, FactMetaData::valueTypeDouble) + , _rangefinderDistanceFact (0, _rangefinderDistanceFactName, FactMetaData::valueTypeDouble) +{ + _addFact(&_camTiltFact, _camTiltFactName); + _addFact(&_tetherTurnsFact, _tetherTurnsFactName); + _addFact(&_lightsLevel1Fact, _lightsLevel1FactName); + _addFact(&_lightsLevel2Fact, _lightsLevel2FactName); + _addFact(&_pilotGainFact, _pilotGainFactName); + _addFact(&_inputHoldFact, _inputHoldFactName); + _addFact(&_rangefinderDistanceFact, _rangefinderDistanceFactName); // Start out as not available "--.--" - _camTiltFact.setRawValue (std::numeric_limits::quiet_NaN()); - _tetherTurnsFact.setRawValue (std::numeric_limits::quiet_NaN()); - _lightsLevel1Fact.setRawValue (std::numeric_limits::quiet_NaN()); - _lightsLevel2Fact.setRawValue (std::numeric_limits::quiet_NaN()); - _pilotGainFact.setRawValue (std::numeric_limits::quiet_NaN()); + _camTiltFact.setRawValue (std::numeric_limits::quiet_NaN()); + _tetherTurnsFact.setRawValue (std::numeric_limits::quiet_NaN()); + _lightsLevel1Fact.setRawValue (std::numeric_limits::quiet_NaN()); + _lightsLevel2Fact.setRawValue (std::numeric_limits::quiet_NaN()); + _pilotGainFact.setRawValue (std::numeric_limits::quiet_NaN()); + _inputHoldFact.setRawValue (std::numeric_limits::quiet_NaN()); + _rangefinderDistanceFact.setRawValue (std::numeric_limits::quiet_NaN()); + } QString ArduSubFirmwarePlugin::vehicleImageOpaque(const Vehicle* vehicle) const diff --git a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h index c28ca691049e1303e490a88018ecb3edda7a76d4..c03062b99aea085c6d232df29b779b5cf3552b67 100644 --- a/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h +++ b/src/FirmwarePlugin/APM/ArduSubFirmwarePlugin.h @@ -35,23 +35,29 @@ class APMSubmarineFactGroup : public FactGroup public: APMSubmarineFactGroup(QObject* parent = NULL); - Q_PROPERTY(Fact* camTilt READ camTilt CONSTANT) - Q_PROPERTY(Fact* tetherTurns READ tetherTurns CONSTANT) - Q_PROPERTY(Fact* lightsLevel1 READ lightsLevel1 CONSTANT) - Q_PROPERTY(Fact* lightsLevel2 READ lightsLevel2 CONSTANT) - Q_PROPERTY(Fact* pilotGain READ pilotGain CONSTANT) - - Fact* camTilt (void) { return &_camTiltFact; } - Fact* tetherTurns (void) { return &_tetherTurnsFact; } - Fact* lightsLevel1 (void) { return &_lightsLevel1Fact; } - Fact* lightsLevel2 (void) { return &_lightsLevel2Fact; } - Fact* pilotGain (void) { return &_pilotGainFact; } + Q_PROPERTY(Fact* camTilt READ camTilt CONSTANT) + Q_PROPERTY(Fact* tetherTurns READ tetherTurns CONSTANT) + Q_PROPERTY(Fact* lightsLevel1 READ lightsLevel1 CONSTANT) + Q_PROPERTY(Fact* lightsLevel2 READ lightsLevel2 CONSTANT) + Q_PROPERTY(Fact* pilotGain READ pilotGain CONSTANT) + Q_PROPERTY(Fact* inputHold READ inputHold CONSTANT) + Q_PROPERTY(Fact* rangefinderDistance READ rangefinderDistance CONSTANT) + + Fact* camTilt (void) { return &_camTiltFact; } + Fact* tetherTurns (void) { return &_tetherTurnsFact; } + Fact* lightsLevel1 (void) { return &_lightsLevel1Fact; } + Fact* lightsLevel2 (void) { return &_lightsLevel2Fact; } + Fact* pilotGain (void) { return &_pilotGainFact; } + Fact* inputHold (void) { return &_inputHoldFact; } + Fact* rangefinderDistance (void) { return &_rangefinderDistanceFact; } static const char* _camTiltFactName; static const char* _tetherTurnsFactName; static const char* _lightsLevel1FactName; static const char* _lightsLevel2FactName; static const char* _pilotGainFactName; + static const char* _inputHoldFactName; + static const char* _rangefinderDistanceFactName; static const char* _settingsGroup; @@ -61,6 +67,8 @@ private: Fact _lightsLevel1Fact; Fact _lightsLevel2Fact; Fact _pilotGainFact; + Fact _inputHoldFact; + Fact _rangefinderDistanceFact; }; class APMSubMode : public APMCustomMode @@ -100,6 +108,8 @@ class ArduSubFirmwarePlugin : public APMFirmwarePlugin public: ArduSubFirmwarePlugin(void); + QList supportedMissionCommands(void); + // Overrides from FirmwarePlugin int manualControlReservedButtonCount(void) final; diff --git a/src/FirmwarePlugin/APM/MavCmdInfoCommon.json b/src/FirmwarePlugin/APM/MavCmdInfoCommon.json index a022d3de848c535fd280e4874a78ef8eb176f7bd..4a508187203b0f1d6ecf82914161cfc95a7fdf94 100644 --- a/src/FirmwarePlugin/APM/MavCmdInfoCommon.json +++ b/src/FirmwarePlugin/APM/MavCmdInfoCommon.json @@ -51,9 +51,9 @@ } }, { - "id": 201, - "comment": "MAV_CMD_DO_SET_ROI", - "paramRemove": "2,3" + "id": 201, + "comment": "MAV_CMD_DO_SET_ROI", + "paramRemove": "1,2,3" }, { "id": 205, diff --git a/src/FirmwarePlugin/FirmwarePlugin.cc b/src/FirmwarePlugin/FirmwarePlugin.cc index 162ccc1fe36ced2fbdd482d185a8bc7e935aaadd..96efc3a1015fcf9d127f69aaca137f60e84daef9 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.cc +++ b/src/FirmwarePlugin/FirmwarePlugin.cc @@ -335,6 +335,7 @@ const QVariantList &FirmwarePlugin::toolBarIndicators(const Vehicle* vehicle) _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/RCRSSIIndicator.qml"))); _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/BatteryIndicator.qml"))); _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/ModeIndicator.qml"))); + _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/VTOLModeIndicator.qml"))); _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/ArmedIndicator.qml"))); _toolBarIndicatorList.append(QVariant::fromValue(QUrl::fromUserInput("qrc:/toolbar/GPSRTKIndicator.qml"))); } @@ -348,6 +349,18 @@ const QVariantList& FirmwarePlugin::cameraList(const Vehicle* vehicle) if (_cameraList.size() == 0) { CameraMetaData* metaData; + metaData = new CameraMetaData(tr("Sony NEX-5R 20mm"), //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications + 23.2, //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications + 15.4, + 4912, + 3264, + 20, + true, + false, + 1, + this); + _cameraList.append(QVariant::fromValue(metaData)); + metaData = new CameraMetaData(tr("Sony ILCE-QX1"), //http://www.sony.co.uk/electronics/interchangeable-lens-cameras/ilce-qx1-body-kit/specifications 23.2, //http://www.sony.com/electronics/camera-lenses/sel16f28/specifications 15.4, @@ -455,6 +468,42 @@ const QVariantList& FirmwarePlugin::cameraList(const Vehicle* vehicle) 0, // minTriggerInterval this); _cameraList.append(QVariant::fromValue(metaData)); + + metaData = new CameraMetaData(tr("Parrot Sequioa RGB"), + 6.17, // sensorWidth + 4.63, // sendsorHeight + 4608, // imageWidth + 3456, // imageHeight + 4.9, // focalLength + true, // landscape + false, // fixedOrientation + 1, // minTriggerInterval + this); + _cameraList.append(QVariant::fromValue(metaData)); + + metaData = new CameraMetaData(tr("Parrot Sequioa Monochrome"), + 4.8, // sensorWidth + 3.6, // sendsorHeight + 1280, // imageWidth + 960, // imageHeight + 4.0, // focalLength + true, // landscape + false, // fixedOrientation + 0.8, // minTriggerInterval + this); + _cameraList.append(QVariant::fromValue(metaData)); + + metaData = new CameraMetaData(tr("GoPro Hero 4"), + 6.17, // sensorWidth + 4.55, // sendsorHeight + 4000, // imageWidth + 3000, // imageHeight + 2.98, // focalLength + true, // landscape + false, // fixedOrientation + 0, // minTriggerInterval + this); + _cameraList.append(QVariant::fromValue(metaData)); } return _cameraList; @@ -583,4 +632,9 @@ QGCCameraControl* FirmwarePlugin::createCameraControl(const mavlink_camera_infor return NULL; } +uint32_t FirmwarePlugin::highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) +{ + // Standard implementation assumes no special handling. Upper part of 32 bit value is not used. + return hlCustomMode; +} diff --git a/src/FirmwarePlugin/FirmwarePlugin.h b/src/FirmwarePlugin/FirmwarePlugin.h index d549fdd65286d90fc9bdeeaf29e4bd70ef440e14..461020d514b4ac711c733267e08ad7c8a02649e3 100644 --- a/src/FirmwarePlugin/FirmwarePlugin.h +++ b/src/FirmwarePlugin/FirmwarePlugin.h @@ -43,11 +43,10 @@ public: /// Set of optional capabilites which firmware may support typedef enum { SetFlightModeCapability = 1 << 0, ///< FirmwarePlugin::setFlightMode method is supported - MavCmdPreflightStorageCapability = 1 << 1, ///< MAV_CMD_PREFLIGHT_STORAGE is supported - PauseVehicleCapability = 1 << 2, ///< Vehicle supports pausing at current location - GuidedModeCapability = 1 << 3, ///< Vehicle supports guided mode commands - OrbitModeCapability = 1 << 4, ///< Vehicle supports orbit mode - TakeoffVehicleCapability = 1 << 5, ///< Vehicle supports guided takeoff + PauseVehicleCapability = 1 << 1, ///< Vehicle supports pausing at current location + GuidedModeCapability = 1 << 2, ///< Vehicle supports guided mode commands + OrbitModeCapability = 1 << 3, ///< Vehicle supports orbit mode + TakeoffVehicleCapability = 1 << 4, ///< Vehicle supports guided takeoff } FirmwareCapabilities; /// Maps from on parameter name to another @@ -130,6 +129,9 @@ public: /// Command vehicle to takeoff from current location to a firmware specific height. virtual void guidedModeTakeoff(Vehicle* vehicle, double takeoffAltRel); + /// @return The minimum takeoff altitude (relative) for guided takeoff. + virtual double minimumTakeoffAltitude(Vehicle* vehicle) { Q_UNUSED(vehicle); return 10; } + /// Command the vehicle to start the mission virtual void startMission(Vehicle* vehicle); @@ -215,6 +217,11 @@ public: /// call deleteParameterMetaData when no longer needed. virtual QObject* loadParameterMetaData(const QString& metaDataFile) { Q_UNUSED(metaDataFile); return NULL; } + /// Returns the FactMetaData associated with the parameter name + /// @param opaqueParameterMetaData Opaque pointer returned from loadParameterMetaData + /// @param name Parameter name + virtual FactMetaData* getMetaDataForFact(QObject* parameterMetaData, const QString& name, MAV_TYPE vehicleType) { Q_UNUSED(parameterMetaData); Q_UNUSED(name); Q_UNUSED(vehicleType); return NULL; } + /// Adds the parameter meta data to the Fact /// @param opaqueParameterMetaData Opaque pointer returned from loadParameterMetaData virtual void addMetaDataToFact(QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) { Q_UNUSED(parameterMetaData); Q_UNUSED(fact); Q_UNUSED(vehicleType); return; } @@ -257,6 +264,7 @@ public: virtual QString vehicleImageCompass(const Vehicle* vehicle) const; /// Allows the core plugin to override the toolbar indicators + /// signals toolbarIndicatorsChanged /// @return A list of QUrl with the indicators (see MainToolBarIndicators.qml) virtual const QVariantList& toolBarIndicators(const Vehicle* vehicle); @@ -295,9 +303,15 @@ public: /// Returns true if the vehicle is a VTOL virtual bool isVtol(const Vehicle* vehicle) const; + /// Convert from HIGH_LATENCY2.custom_mode value to correct 32 bit value. + virtual uint32_t highLatencyCustomModeTo32Bits(uint16_t hlCustomMode); + // FIXME: Hack workaround for non pluginize FollowMe support static const QString px4FollowMeFlightMode; +signals: + void toolbarIndicatorsChanged(void); + protected: // Arms the vehicle with validation and retries // @return: true - vehicle armed, false - vehicle failed to arm diff --git a/src/FirmwarePlugin/PX4/MavCmdInfoCommon.json b/src/FirmwarePlugin/PX4/MavCmdInfoCommon.json index 90d622990a0f17008ebd07d3ef7c3bebe3680ef3..f50fe22108663f9d810aa67947d470531eb34589 100644 --- a/src/FirmwarePlugin/PX4/MavCmdInfoCommon.json +++ b/src/FirmwarePlugin/PX4/MavCmdInfoCommon.json @@ -13,6 +13,11 @@ "id": 21, "comment": "MAV_CMD_NAV_LAND", "paramRemove": "1" + }, + { + "id": 201, + "comment": "MAV_CMD_DO_SET_ROI", + "paramRemove": "1,2,3" } ] } diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc index 135969f9bab83fc7ea15fdcfc08271b92ceec7f0..602fc0dfa0bdb39190699ec1860a22b63d883e68 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.cc @@ -227,7 +227,7 @@ int PX4FirmwarePlugin::manualControlReservedButtonCount(void) bool PX4FirmwarePlugin::isCapable(const Vehicle *vehicle, FirmwareCapabilities capabilities) { - int available = MavCmdPreflightStorageCapability | SetFlightModeCapability | PauseVehicleCapability | GuidedModeCapability; + int available = SetFlightModeCapability | PauseVehicleCapability | GuidedModeCapability; if (vehicle->multiRotor() || vehicle->vtol()) { available |= TakeoffVehicleCapability; } @@ -247,6 +247,19 @@ bool PX4FirmwarePlugin::sendHomePositionToVehicle(void) return false; } +FactMetaData* PX4FirmwarePlugin::getMetaDataForFact(QObject* parameterMetaData, const QString& name, MAV_TYPE vehicleType) +{ + PX4ParameterMetaData* px4MetaData = qobject_cast(parameterMetaData); + + if (px4MetaData) { + return px4MetaData->getMetaDataForFact(name, vehicleType); + } else { + qWarning() << "Internal error: pointer passed to PX4FirmwarePlugin::getMetaDataForFact not PX4ParameterMetaData"; + } + + return NULL; +} + void PX4FirmwarePlugin::addMetaDataToFact(QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) { PX4ParameterMetaData* px4MetaData = qobject_cast(parameterMetaData); @@ -258,6 +271,11 @@ void PX4FirmwarePlugin::addMetaDataToFact(QObject* parameterMetaData, Fact* fact } } +void PX4FirmwarePlugin::getParameterMetaDataVersionInfo(const QString& metaDataFile, int& majorVersion, int& minorVersion) +{ + return PX4ParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); +} + QList PX4FirmwarePlugin::supportedMissionCommands(void) { QList list; @@ -272,7 +290,7 @@ QList PX4FirmwarePlugin::supportedMissionCommands(void) << MAV_CMD_DO_SET_SERVO << MAV_CMD_DO_CHANGE_SPEED << MAV_CMD_DO_LAND_START - << MAV_CMD_DO_SET_ROI + << MAV_CMD_DO_SET_ROI_LOCATION << MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET << MAV_CMD_DO_SET_ROI_NONE << MAV_CMD_DO_MOUNT_CONFIGURE << MAV_CMD_DO_MOUNT_CONTROL << MAV_CMD_SET_CAMERA_MODE @@ -384,24 +402,30 @@ void PX4FirmwarePlugin::_mavCommandResult(int vehicleId, int component, int comm } } -void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double takeoffAltRel) +double PX4FirmwarePlugin::minimumTakeoffAltitude(Vehicle* vehicle) { QString takeoffAltParam("MIS_TAKEOFF_ALT"); + if (vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, takeoffAltParam)) { + return vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, takeoffAltParam)->rawValue().toDouble(); + } else { + return FirmwarePlugin::minimumTakeoffAltitude(vehicle); + } +} + +void PX4FirmwarePlugin::guidedModeTakeoff(Vehicle* vehicle, double takeoffAltRel) +{ double vehicleAltitudeAMSL = vehicle->altitudeAMSL()->rawValue().toDouble(); if (qIsNaN(vehicleAltitudeAMSL)) { qgcApp()->showMessage(tr("Unable to takeoff, vehicle position not known.")); return; } - if (!vehicle->parameterManager()->parameterExists(FactSystem::defaultComponentId, takeoffAltParam)) { - qgcApp()->showMessage(tr("Unable to takeoff, MIS_TAKEOFF_ALT parameter missing.")); - return; - } - - double takeoffAltRelFromVehicle = vehicle->parameterManager()->getParameter(FactSystem::defaultComponentId, takeoffAltParam)->rawValue().toDouble(); + double takeoffAltRelFromVehicle = minimumTakeoffAltitude(vehicle); double takeoffAltAMSL = qMax(takeoffAltRel, takeoffAltRelFromVehicle) + vehicleAltitudeAMSL; + qDebug() << takeoffAltRel << takeoffAltRelFromVehicle << takeoffAltAMSL << vehicleAltitudeAMSL; + connect(vehicle, &Vehicle::mavCommandResult, this, &PX4FirmwarePlugin::_mavCommandResult); vehicle->sendMavCommand(vehicle->defaultComponentId(), MAV_CMD_NAV_TAKEOFF, @@ -566,4 +590,11 @@ QGCCameraControl* PX4FirmwarePlugin::createCameraControl(const mavlink_camera_in return new QGCCameraControl(info, vehicle, compID, parent); } +uint32_t PX4FirmwarePlugin::highLatencyCustomModeTo32Bits(uint16_t hlCustomMode) +{ + union px4_custom_mode px4_cm; + px4_cm.data = 0; + px4_cm.custom_mode_hl = hlCustomMode; + return px4_cm.data; +} diff --git a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h index efce082b3240271cd3ab88d5cfc45d4799d99dce..6497793e9295da58596cfaca371180d69f99c23c 100644 --- a/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h +++ b/src/FirmwarePlugin/PX4/PX4FirmwarePlugin.h @@ -49,16 +49,18 @@ public: void guidedModeOrbit (Vehicle* vehicle, const QGeoCoordinate& centerCoord = QGeoCoordinate(), double radius = NAN, double velocity = NAN, double altitude = NAN) override; void guidedModeGotoLocation (Vehicle* vehicle, const QGeoCoordinate& gotoCoord) override; void guidedModeChangeAltitude (Vehicle* vehicle, double altitudeRel) override; + double minimumTakeoffAltitude (Vehicle* vehicle) override; void startMission (Vehicle* vehicle) override; bool isGuidedMode (const Vehicle* vehicle) const override; int manualControlReservedButtonCount(void) override; void initializeVehicle (Vehicle* vehicle) override; bool sendHomePositionToVehicle (void) override; void addMetaDataToFact (QObject* parameterMetaData, Fact* fact, MAV_TYPE vehicleType) override; + FactMetaData* getMetaDataForFact (QObject* parameterMetaData, const QString& name, MAV_TYPE vehicleType) override; QString missionCommandOverrides (MAV_TYPE vehicleType) const override; QString getVersionParam (void) override { return QString("SYS_PARAM_VER"); } QString internalParameterMetaDataFile (Vehicle* vehicle) override { Q_UNUSED(vehicle); return QString(":/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml"); } - void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) override { PX4ParameterMetaData::getParameterMetaDataVersionInfo(metaDataFile, majorVersion, minorVersion); } + void getParameterMetaDataVersionInfo (const QString& metaDataFile, int& majorVersion, int& minorVersion) override; QObject* loadParameterMetaData (const QString& metaDataFile) final; bool adjustIncomingMavlinkMessage (Vehicle* vehicle, mavlink_message_t* message) override; QString offlineEditingParamFile(Vehicle* vehicle) override { Q_UNUSED(vehicle); return QStringLiteral(":/FirmwarePlugin/PX4/PX4.OfflineEditing.params"); } @@ -68,6 +70,7 @@ public: QString autoDisarmParameter (Vehicle* vehicle) override { Q_UNUSED(vehicle); return QStringLiteral("COM_DISARM_LAND"); } QGCCameraManager* createCameraManager (Vehicle* vehicle) override; QGCCameraControl* createCameraControl (const mavlink_camera_information_t* info, Vehicle* vehicle, int compID, QObject* parent = NULL) override; + uint32_t highLatencyCustomModeTo32Bits (uint16_t hlCustomMode) override; protected: typedef struct { diff --git a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml index 939342568036656cb068edb029c90b45d6593413..cd61fcbf0db8402743a2a4b8238b93e6d7e98b4a 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml +++ b/src/FirmwarePlugin/PX4/PX4ParameterFactMetaData.xml @@ -137,84 +137,33 @@ 0 - - - Body angular rate process noise - examples/attitude_estimator_ekf - - - Body angular acceleration process noise - examples/attitude_estimator_ekf - - - Acceleration process noise - examples/attitude_estimator_ekf - - - Magnet field vector process noise - examples/attitude_estimator_ekf - - - Gyro measurement noise - examples/attitude_estimator_ekf - - - Accel measurement noise - examples/attitude_estimator_ekf - - - Mag measurement noise - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (1, 1) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (2, 2) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia matrix diagonal entry (3, 3) - kg*m^2 - examples/attitude_estimator_ekf - - - Moment of inertia enabled in estimator - If set to != 0 the moment of inertia will be used in the estimator - - examples/attitude_estimator_ekf - - - - Complimentary filter accelerometer weight - 0 - 1 - 2 - modules/attitude_estimator_q - - - Complimentary filter magnetometer weight - 0 - 1 - 2 + + Acceleration compensation based on GPS +velocity + modules/attitude_estimator_q - - Complimentary filter external heading weight + + Gyro bias limit 0 - 1 + 2 + rad/s + 3 modules/attitude_estimator_q - - Complimentary filter gyroscope bias weight + + External heading usage mode (from Motion capture/Vision) +Set to 1 to use heading estimate from vision. +Set to 2 to use heading from motion capture 0 - 1 - 2 + 2 modules/attitude_estimator_q + + None + Vision + Motion Capture + Magnetic declination, in degrees @@ -228,111 +177,64 @@ modules/attitude_estimator_q - - External heading usage mode (from Motion capture/Vision) -Set to 1 to use heading estimate from vision. -Set to 2 to use heading from motion capture + + Complimentary filter accelerometer weight 0 - 2 + 1 + 2 modules/attitude_estimator_q - - Vision - None - Motion Capture - - - Acceleration compensation based on GPS -velocity - + + Complimentary filter external heading weight + 0 + 1 modules/attitude_estimator_q - - Gyro bias limit + + Complimentary filter gyroscope bias weight 0 - 2 - rad/s - 3 + 1 + 2 + modules/attitude_estimator_q + + + Complimentary filter magnetometer weight + 0 + 1 + 2 modules/attitude_estimator_q - - Scaling factor for battery voltage sensor on PX4IO - 1 - 100000 - modules/sensors - - - Scaling from ADC counts to volt on the ADC input (battery voltage) - This is not the battery voltage, but the intermediate ADC voltage. A value of -1 signifies that the board defaults are used, which is highly recommended. + + Battery current per volt (A/V) + The voltage seen by the 3.3V ADC multiplied by this factor will determine the battery current. A value of -1 means to use the board default. 8 modules/sensors + + Battery capacity + Defines the capacity of the attached battery. + -1.0 + 100000 + mAh + 0 + 50 + true + modules/systemlib + Scaling from ADC counts to volt on the ADC input (battery current) This is not the battery current, but the intermediate ADC voltage. A value of -1 signifies that the board defaults are used, which is highly recommended. 8 modules/sensors - - Offset in volt as seen by the ADC input of the current sensor - This offset will be subtracted before calculating the battery current based on the voltage. - 8 - modules/sensors - - - Battery voltage divider (V divider) - This is the divider from battery voltage to 3.3V ADC voltage. If using e.g. Mauch power modules the value from the datasheet can be applied straight here. A value of -1 means to use the board default. - 8 - modules/sensors - - - Battery current per volt (A/V) - The voltage seen by the 3.3V ADC multiplied by this factor will determine the battery current. A value of -1 means to use the board default. + + Scaling from ADC counts to volt on the ADC input (battery voltage) + This is not the battery voltage, but the intermediate ADC voltage. A value of -1 signifies that the board defaults are used, which is highly recommended. 8 modules/sensors - - Battery monitoring source - This parameter controls the source of battery data. The value 'Power Module' means that measurements are expected to come from a power module. If the value is set to 'External' then the system expects to receive mavlink battery status messages. - 0 - 1 - modules/sensors - - External - Power Module - - - - Empty cell voltage (5C load) - Defines the voltage where a single cell of the battery is considered empty. The voltage should be chosen before the steep dropoff to 2.8V. A typical lithium battery can only be discharged down to 10% before it drops off to a voltage level damaging the cells. - V - 2 - 0.01 - true - modules/systemlib - - - Full cell voltage (5C load) - Defines the voltage where a single cell of the battery is considered full under a mild load. This will never be the nominal voltage of 4.2V - V - 2 - 0.01 - true - modules/systemlib - - - Low threshold - Sets the threshold when the battery will be reported as low. This has to be higher than the critical threshold. - 0.12 - 0.4 - norm - 2 - 0.01 - true - modules/systemlib - Critical threshold Sets the threshold when the battery will be reported as critically low. This has to be lower than the low threshold. This threshold commonly will trigger RTL. @@ -355,26 +257,17 @@ velocity true modules/systemlib - - Voltage drop per cell on full throttle - This implicitely defines the internal resistance to maximum current ratio and assumes linearity. A good value to use is the difference between the 5C and 20-25C load. Not used if BAT_R_INTERNAL is set. - 0.07 - 0.5 - V + + Low threshold + Sets the threshold when the battery will be reported as low. This has to be higher than the critical threshold. + 0.12 + 0.4 + norm 2 0.01 true modules/systemlib - - Explicitly defines the per cell internal resistance - If non-negative, then this will be used in place of BAT_V_LOAD_DROP for all calculations. - -1.0 - 0.2 - Ohms - true - modules/systemlib - Number of cells Defines the number of cells the attached battery consists of. @@ -382,82 +275,100 @@ velocity true modules/systemlib - 11S Battery - 10S Battery - 13S Battery - 12S Battery - 15S Battery - 14S Battery - 16S Battery Unconfigured - 3S Battery 2S Battery - 5S Battery + 3S Battery 4S Battery - 7S Battery + 5S Battery 6S Battery - 9S Battery + 7S Battery 8S Battery + 9S Battery + 10S Battery + 11S Battery + 12S Battery + 13S Battery + 14S Battery + 15S Battery + 16S Battery - - Battery capacity - Defines the capacity of the attached battery. + + Explicitly defines the per cell internal resistance + If non-negative, then this will be used in place of BAT_V_LOAD_DROP for all calculations. -1.0 - 100000 - mA - 0 - 50 + 0.2 + Ohms true modules/systemlib - - - - Camera feedback mode - Sets the camera feedback mode. + + Battery monitoring source + This parameter controls the source of battery data. The value 'Power Module' means that measurements are expected to come from a power module. If the value is set to 'External' then the system expects to receive mavlink battery status messages. 0 1 - modules/camera_feedback + modules/sensors - Feedback on trigger - Disabled + Power Module + External - - - - Camera trigger Interface - Selects the trigger interface + + Full cell voltage (5C load) + Defines the voltage where a single cell of the battery is considered full under a mild load. This will never be the nominal voltage of 4.2V + V + 2 + 0.01 true - drivers/camera_trigger - - GPIO - MAVLink (forward via MAV_CMD_IMAGE_START_CAPTURE) - Seagull MAP2 (over PWM) - Generic PWM (IR trigger, servo) - + modules/systemlib - - Camera trigger interval - This parameter sets the time between two consecutive trigger events - 4.0 - 10000.0 - ms - 1 - drivers/camera_trigger + + Battery voltage divider (V divider) + This is the divider from battery voltage to 3.3V ADC voltage. If using e.g. Mauch power modules the value from the datasheet can be applied straight here. A value of -1 means to use the board default. + 8 + modules/sensors - - Camera trigger polarity - This parameter sets the polarity of the trigger (0 = active low, 1 = active high ) + + Empty cell voltage (5C load) + Defines the voltage where a single cell of the battery is considered empty. The voltage should be chosen before the steep dropoff to 2.8V. A typical lithium battery can only be discharged down to 10% before it drops off to a voltage level damaging the cells. + V + 2 + 0.01 + true + modules/systemlib + + + Voltage drop per cell on full throttle + This implicitely defines the internal resistance to maximum current ratio and assumes linearity. A good value to use is the difference between the 5C and 20-25C load. Not used if BAT_R_INTERNAL is set. + 0.07 + 0.5 + V + 2 + 0.01 + true + modules/systemlib + + + Offset in volt as seen by the ADC input of the current sensor + This offset will be subtracted before calculating the battery current based on the voltage. + 8 + modules/sensors + + + + + Camera feedback mode + Sets the camera feedback mode. 0 1 - drivers/camera_trigger + modules/camera_feedback - Active high - Active low + Disabled + Feedback on trigger + + Camera trigger activation time This parameter sets the time the trigger needs to pulled high or low. @@ -467,6 +378,36 @@ velocity 1 drivers/camera_trigger + + Camera trigger distance + Sets the distance at which to trigger the camera. + 0 + m + 1 + 1 + drivers/camera_trigger + + + Camera trigger Interface + Selects the trigger interface + true + drivers/camera_trigger + + GPIO + Seagull MAP2 (over PWM) + MAVLink (forward via MAV_CMD_IMAGE_START_CAPTURE) + Generic PWM (IR trigger, servo) + + + + Camera trigger interval + This parameter sets the time between two consecutive trigger events + 4.0 + 10000.0 + ms + 1 + drivers/camera_trigger + Camera trigger mode 0 @@ -474,10 +415,10 @@ velocity true drivers/camera_trigger - Time based, on command Disable - Distance based, always on + Time based, on command Time based, always on + Distance based, always on Distance based, on command (Survey mode) @@ -490,82 +431,84 @@ velocity true drivers/camera_trigger - - Camera trigger distance - Sets the distance at which to trigger the camera. + + Camera trigger polarity + This parameter sets the polarity of the trigger (0 = active low, 1 = active high ) 0 - m - 1 - 1 + 1 drivers/camera_trigger + + Active low + Active high + - - Circuit breaker for power supply check - Setting this parameter to 894281 will disable the power valid checks in the commander. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for airspeed sensor + Setting this parameter to 162128 will disable the check for an airspeed sensor. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 894281 + 162128 true modules/systemlib - - Circuit breaker for rate controller output - Setting this parameter to 140253 will disable the rate controller uORB publication. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for disabling buzzer + Setting this parameter to 782097 will disable the buzzer audio notification. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 140253 + 782097 true modules/systemlib - - Circuit breaker for IO safety - Setting this parameter to 22027 will disable IO safety. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for engine failure detection + Setting this parameter to 284953 will disable the engine failure detection. If the aircraft is in engine failure mode the engine failure flag will be set to healthy WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 22027 + 284953 true modules/systemlib - - Circuit breaker for airspeed sensor - Setting this parameter to 162128 will disable the check for an airspeed sensor. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for flight termination + Setting this parameter to 121212 will disable the flight termination action. --> The IO driver will not do flight termination if requested by the FMU WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 162128 + 121212 true modules/systemlib - - Circuit breaker for flight termination - Setting this parameter to 121212 will disable the flight termination action. --> The IO driver will not do flight termination if requested by the FMU WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for GPS failure detection + Setting this parameter to 240024 will disable the GPS failure detection. If this check is enabled, then the sensor check will fail if the GPS module is missing. It will also check for excessive signal noise on the GPS receiver and warn the user if detected. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 121212 + 240024 true modules/systemlib - - Circuit breaker for engine failure detection - Setting this parameter to 284953 will disable the engine failure detection. If the aircraft is in engine failure mode the engine failure flag will be set to healthy WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for IO safety + Setting this parameter to 22027 will disable IO safety. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 284953 + 22027 true modules/systemlib - - Circuit breaker for GPS failure detection - Setting this parameter to 240024 will disable the GPS failure detection. If this check is enabled, then the sensor check will fail if the GPS module is missing. It will also check for excessive signal noise on the GPS receiver and warn the user if detected. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for rate controller output + Setting this parameter to 140253 will disable the rate controller uORB publication. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 240024 + 140253 true modules/systemlib - - Circuit breaker for disabling buzzer - Setting this parameter to 782097 will disable the buzzer audio notification. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK + + Circuit breaker for power supply check + Setting this parameter to 894281 will disable the power valid checks in the commander. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 - 782097 + 894281 true modules/systemlib - + Circuit breaker for USB link check Setting this parameter to 197848 will disable the USB connected checks in the commander. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 @@ -573,7 +516,7 @@ velocity true modules/systemlib - + Circuit breaker for position error check Setting this parameter to 201607 will disable the position and velocity accuracy checks in the commander. WARNING: ENABLING THIS CIRCUIT BREAKER IS AT OWN RISK 0 @@ -583,113 +526,120 @@ velocity - - Datalink loss time threshold - After this amount of seconds without datalink the data link lost mode triggers - 5 - 300 - s - 1 - 0.5 + + Arm authorization parameters, this uint32_t will be splitted between starting from the LSB: +- 8bits to authorizer system id +- 16bits to authentication method parameter, this will be used to store a timeout for the first 2 methods but can be used to another parameter for other new authentication methods. +- 7bits to authentication method +- one arm = 0 +- two step arm = 1 +* the MSB bit is not used to avoid problems in the conversion between int and uint + Default value: (10 << 0 | 1000 << 8 | 0 << 24) = 256010 - authorizer system id = 10 - authentication method parameter = 10000msec of timeout - authentication method = during arm modules/commander - - Datalink regain time threshold - After a data link loss: after this this amount of seconds with a healthy datalink the 'datalink loss' flag is set back to false - 0 - 3 - s - 1 - 0.5 + + Maximum value of EKF accelerometer delta velocity bias estimate that will allow arming. +Note: ekf2 will limit the delta velocity bias estimate magnitude to be less than EKF2_ABL_LIM * FILTER_UPDATE_PERIOD_MS * 0.001 so this parameter must be less than that to be useful + 0.001 + 0.01 + m/s + 4 + 0.0001 modules/commander - - Engine Failure Throttle Threshold - Engine failure triggers only above this throttle value - 0.0 - 1.0 - norm - 2 - 0.01 + + Maximum value of EKF gyro delta angle bias estimate that will allow arming + 0.0001 + 0.0017 + rad + 5 + 0.0001 modules/commander - - Engine Failure Current/Throttle Threshold - Engine failure triggers only below this current value - 0.0 - 50.0 - A/% + + Maximum EKF height innovation test ratio that will allow arming + 0.1 + 1.0 + m 2 - 1 + 0.05 modules/commander - - Engine Failure Time Threshold - Engine failure triggers only if the throttle threshold and the current to throttle threshold are violated for this time - 0.0 - 60.0 - s - 1 - 1 + + Maximum EKF position innovation test ratio that will allow arming + 0.1 + 1.0 + m + 2 + 0.05 modules/commander - - RC loss time threshold - After this amount of seconds without RC connection the rc lost flag is set to true - 0 - 35 - s - 1 - 0.1 + + Maximum EKF velocity innovation test ratio that will allow arming + 0.1 + 1.0 + m/s + 2 + 0.05 modules/commander - - RC stick override threshold - If an RC stick is moved more than by this amount the system will interpret this as override request by the pilot. - 5 - 40 - % - 0 + + Maximum EKF yaw innovation test ratio that will allow arming + 0.1 + 1.0 + rad + 2 0.05 modules/commander - - Home set horizontal threshold - The home position will be set if the estimated positioning accuracy is below the threshold. - 2 - 15 - m + + Maximum accelerometer inconsistency between IMU units that will allow arming + 0.1 + 1.0 + m/s/s 2 - 0.5 + 0.05 modules/commander - - Home set vertical threshold - The home position will be set if the estimated positioning accuracy is below the threshold. - 5 - 25 - m + + Maximum rate gyro inconsistency between IMU units that will allow arming + 0.02 + 0.3 + rad/s + 3 + 0.01 + modules/commander + + + Maximum magnetic field inconsistency between units that will allow arming + 0.05 + 0.5 + Gauss 2 - 0.5 + 0.05 modules/commander - - RC control input mode - The default value of 0 requires a valid RC transmitter setup. Setting this to 1 allows joystick control and disables RC input handling and the associated checks. A value of 2 will generate RC control data from manual input received via MAVLink instead of directly forwarding the manual input data. + + Require valid mission to arm + The default allows to arm the vehicle without a valid mission. + + modules/commander + + + Arm switch is only a button + The default uses the arm switch as real switch. If parameter set button gets handled like stick arming. 0 - 2 + 1 modules/commander - Joystick/No RC Checks - RC Transmitter - Virtual RC by Joystick + Arm switch is a switch that stays on when armed + Arm switch is a button that only triggers arming and disarming - - RC input arm/disarm command duration - The default value of 1000 requires the stick to be held in the arm or disarm position for 1 second. - 100 - 1500 + + Allow arming without GPS + The default allows to arm the vehicle without GPS signal. + modules/commander @@ -702,64 +652,81 @@ velocity 1 modules/commander - - Allow arming without GPS - The default allows to arm the vehicle without GPS signal. - + + Datalink loss time threshold + After this amount of seconds without datalink the data link lost mode triggers + 5 + 300 + s + 1 + 0.5 modules/commander - - Arm switch is only a button - The default uses the arm switch as real switch. If parameter set button gets handled like stick arming. + + Datalink regain time threshold + After a data link loss: after this this amount of seconds with a healthy datalink the 'datalink loss' flag is set back to false 0 - 1 + 3 + s + 1 + 0.5 modules/commander - - Arm switch is a button that only triggers arming and disarming - Arm switch is a switch that stays on when armed - - - Battery failsafe mode - Action the system takes on low battery. Defaults to off - 0 + + Engine Failure Current/Throttle Threshold + Engine failure triggers only below this current value + 0.0 + 50.0 + A/% + 2 1 modules/commander - - Return to land - Warning - Return to land at critically low level, land at current position if reaching dangerously low levels - Land at current position - - - Time-out to wait when offboard connection is lost before triggering offboard lost action. -See COM_OBL_ACT and COM_OBL_RC_ACT to configure action - 0 - 60 - second + + Engine Failure Throttle Threshold + Engine failure triggers only above this throttle value + 0.0 + 1.0 + norm + 2 + 0.01 + modules/commander + + + Engine Failure Time Threshold + Engine failure triggers only if the throttle threshold and the current to throttle threshold are violated for this time + 0.0 + 60.0 + s + 1 1 modules/commander + + Next flight UUID + This number is incremented automatically after every flight on disarming in order to remember the next flight UUID. The first flight is 0. + 0 + modules/commander + First flightmode slot (1000-1160) If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me @@ -767,20 +734,20 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me @@ -788,20 +755,20 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me @@ -809,20 +776,20 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me @@ -830,20 +797,20 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me @@ -851,123 +818,62 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action If the main switch channel is in this range the selected flight mode will be applied. modules/commander - Land - Takeoff - Follow Me - Altitude + Unassigned Manual - Mission + Altitude Position - Unassigned + Mission Hold - Offboard - Acro - Rattitude Return + Acro + Offboard Stabilized + Rattitude + Takeoff + Land + Follow Me - - Maximum EKF position innovation test ratio that will allow arming - 0.1 - 1.0 + + Home set horizontal threshold + The home position will be set if the estimated positioning accuracy is below the threshold. + 2 + 15 m 2 - 0.05 - modules/commander - - - Maximum EKF velocity innovation test ratio that will allow arming - 0.1 - 1.0 - m/s - 2 - 0.05 + 0.5 modules/commander - - Maximum EKF height innovation test ratio that will allow arming - 0.1 - 1.0 + + Home set vertical threshold + The home position will be set if the estimated positioning accuracy is below the threshold. + 5 + 25 m 2 - 0.05 + 0.5 modules/commander - - Maximum EKF yaw innovation test ratio that will allow arming - 0.1 - 1.0 - rad - 2 - 0.05 - modules/commander - - - Maximum value of EKF accelerometer delta velocity bias estimate that will allow arming - 0.001 - 0.01 - m/s - 4 - 0.0005 - modules/commander - - - Maximum value of EKF gyro delta angle bias estimate that will allow arming - 0.0001 - 0.0017 - rad - 5 - 0.0001 - modules/commander - - - Maximum accelerometer inconsistency between IMU units that will allow arming - 0.1 - 1.0 - m/s/s - 2 - 0.05 - modules/commander - - - Maximum rate gyro inconsistency between IMU units that will allow arming - 0.02 - 0.3 - rad/s - 3 - 0.01 - modules/commander - - - Maximum magnetic field inconsistency between units that will allow arming - 0.05 - 0.5 - Gauss - 2 - 0.05 - modules/commander - - - Enable RC stick override of auto modes - - modules/commander - - - Require valid mission to arm - The default allows to arm the vehicle without a valid mission. - + + Battery failsafe mode + Action the system takes on low battery. Defaults to off + 0 + 1 modules/commander + + Warning + Return to land + Land at current position + Return to land at critically low level, land at current position if reaching dangerously low levels + - - Arm authorization parameters, this uint32_t will be splitted between starting from the LSB: -- 8bits to authorizer system id -- 16bits to authentication method parameter, this will be used to store a timeout for the first 2 methods but can be used to another parameter for other new authentication methods. -- 7bits to authentication method -- one arm = 0 -- two step arm = 1 -* the MSB bit is not used to avoid problems in the conversion between int and uint - Default value: (10 << 0 | 1000 << 8 | 0 << 24) = 256010 - authorizer system id = 10 - authentication method parameter = 10000msec of timeout - authentication method = during arm + + Time-out to wait when offboard connection is lost before triggering offboard lost action. +See COM_OBL_ACT and COM_OBL_RC_ACT to configure action + 0 + 60 + second + 1 modules/commander @@ -977,6 +883,12 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action true modules/commander + + Loss of position probation gain factor + This sets the rate that the loss of position probation time grows when position checks are failing. The default value has been optimised for rotary wing applications. For fixed wing applications a value of 0 should be used. + true + modules/commander + Loss of position probation delay at takeoff The probation delay is the number of seconds that the EKF innovation checks need to pass for the position to be declared good after it has been declared bad. The probation delay will be reset to this parameter value when takeoff is detected. After takeoff, if position checks are passing, the probation delay will reduce by one second for every lapsed second of valid position down to a minimum of 1 second. If position checks are failing, the probation delay will increase by COM_POS_FS_GAIN seconds for every lapsed second up to a maximum of 100 seconds. The default value has been optimised for rotary wing applications. For fixed wing applications, a value of 1 should be used. @@ -984,56 +896,77 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action true modules/commander - - Loss of position probation gain factor - This sets the rate that the loss of position probation time grows when position checks are failing. The default value has been optimised for rotary wing applications. For fixed wing applications a value of 0 should be used. - true + + RC input arm/disarm command duration + The default value of 1000 requires the stick to be held in the arm or disarm position for 1 second. + 100 + 1500 modules/commander - - Next flight UUID - This number is incremented automatically after every flight on disarming in order to remember the next flight UUID. The first flight is 0. + + RC control input mode + The default value of 0 requires a valid RC transmitter setup. Setting this to 1 allows joystick control and disables RC input handling and the associated checks. A value of 2 will generate RC control data from manual input received via MAVLink instead of directly forwarding the manual input data. 0 + 2 modules/commander + + RC Transmitter + Joystick/No RC Checks + Virtual RC by Joystick + - - - - Comms hold wait time - The amount of time in seconds the system should wait at the comms hold waypoint - 0.0 - 3600.0 + + RC loss time threshold + After this amount of seconds without RC connection the rc lost flag is set to true + 0 + 35 s + 1 + 0.1 + modules/commander + + + Enable RC stick override of auto modes + + modules/commander + + + RC stick override threshold + If an RC stick is moved more than by this amount the system will interpret this as override request by the pilot. + 5 + 40 + % 0 - 1 + 0.05 + modules/commander + + + + + Airfield home alt + Altitude of airfield home waypoint + -50 + m + 1 + 0.5 modules/navigator - - Comms hold Lat - Latitude of comms hold waypoint + + Airfield home Lat + Latitude of airfield home waypoint -900000000 900000000 deg * 1e7 modules/navigator - - Comms hold Lon - Longitude of comms hold waypoint + + Airfield home Lon + Longitude of airfield home waypoint -1800000000 1800000000 deg * 1e7 modules/navigator - - Comms hold alt - Altitude of comms hold waypoint - -50 - 30000 - m - 1 - 0.5 - modules/navigator - Airfield home wait time The amount of time in seconds the system should wait at the airfield home waypoint @@ -1044,99 +977,162 @@ See COM_OBL_ACT and COM_OBL_RC_ACT to configure action 1 modules/navigator - - Number of allowed Datalink timeouts - After more than this number of data link timeouts the aircraft returns home directly - 0 - 1000 - modules/navigator - Skip comms hold wp If set to 1 the system will skip the comms hold wp on data link loss and will directly fly to airfield home modules/navigator - - Airfield home Lat - Latitude of airfield home waypoint + + Comms hold alt + Altitude of comms hold waypoint + -50 + 30000 + m + 1 + 0.5 + modules/navigator + + + Comms hold Lat + Latitude of comms hold waypoint -900000000 900000000 deg * 1e7 modules/navigator - - Airfield home Lon - Longitude of airfield home waypoint + + Comms hold Lon + Longitude of comms hold waypoint -1800000000 1800000000 deg * 1e7 modules/navigator - - Airfield home alt - Altitude of airfield home waypoint - -50 - m - 1 - 0.5 + + Comms hold wait time + The amount of time in seconds the system should wait at the comms hold waypoint + 0.0 + 3600.0 + s + 0 + 1 + modules/navigator + + + Number of allowed Datalink timeouts + After more than this number of data link timeouts the aircraft returns home directly + 0 + 1000 modules/navigator - - Minimum time of arrival delta between non-IMU observations before data is downsampled. -Baro and Magnetometer data will be averaged before downsampling, other data will be point sampled resulting in loss of information - 10 - 50 - ms + + 1-sigma IMU accelerometer switch-on bias + 0.0 + 0.5 + m/s/s + 2 true modules/ekf2 - - Magnetometer measurement delay relative to IMU measurements - 0 - 300 - ms + + Maximum IMU accel magnitude that allows IMU bias learning. +If the magnitude of the IMU accelerometer vector exceeds this value, the EKF delta velocity state estimation will be inhibited. +This reduces the adverse effect of high manoeuvre accelerations and IMU nonlinerity and scale factor errors on the delta velocity bias estimates + 20.0 + 200.0 + m/s/s 1 - true modules/ekf2 - - Barometer measurement delay relative to IMU measurements - 0 - 300 - ms + + Maximum IMU gyro angular rate magnitude that allows IMU bias learning. +If the magnitude of the IMU angular rate vector exceeds this value, the EKF delta velocity state estimation will be inhibited. +This reduces the adverse effect of rapid rotation rates and associated errors on the delta velocity bias estimates + 2.0 + 20.0 + rad/s 1 - true modules/ekf2 - - GPS measurement delay relative to IMU measurements + + Accelerometer bias learning limit. The ekf delta velocity bias states will be limited to within a range equivalent to +- of this value + 0.0 + 0.8 + m/s/s + 2 + modules/ekf2 + + + Time constant used by acceleration and angular rate magnitude checks used to inhibit delta velocity bias learning. +The vector magnitude of angular rate and acceleration used to check if learning should be inhibited has a peak hold filter applied to it with an exponential decay. +This parameter controls the time constant of the decay + 0.1 + 1.0 + s + 2 + modules/ekf2 + + + Process noise for IMU accelerometer bias prediction + 0.0 + 0.01 + m/s**3 + 6 + modules/ekf2 + + + Accelerometer noise for covariance prediction + 0.01 + 1.0 + m/s/s + 2 + modules/ekf2 + + + Integer bitmask controlling data fusion and aiding methods + Set bits in the following positions to enable: 0 : Set to true to use GPS data if available 1 : Set to true to use optical flow data if available 2 : Set to true to inhibit IMU bias estimation 3 : Set to true to enable vision position fusion 4 : Set to true to enable vision yaw fusion 5 : Set to true to enable multi-rotor drag specific force fusion 6 : set to true if the EV observations are in a non NED reference frame and need to be rotated before being used 0 - 300 - ms - 1 + 127 true modules/ekf2 + + use GPS + use optical flow + inhibit IMU bias estimation + vision position fusion + vision yaw fusion + multi-rotor drag fusion + rotate external vision + - - Optical flow measurement delay relative to IMU measurements -Assumes measurement is timestamped at trailing edge of integration period - 0 - 300 - ms - 1 + + 1-sigma tilt angle uncertainty after gravity vector alignment + 0.0 + 0.5 + rad + 3 true modules/ekf2 - - Range finder measurement delay relative to IMU measurements - 0 - 300 - ms + + Airspeed fusion threshold. A value of zero will deactivate airspeed fusion. Any other positive +value will determine the minimum airspeed which will still be fused. Set to about 90% of the vehicles stall speed. +Both airspeed fusion and sideslip fusion must be active for the EKF to continue navigating after loss of GPS. +Use EKF2_FUSE_BETA to activate sideslip fusion + 0.0 + m/s + 1 + modules/ekf2 + + + Upper limit on airspeed along individual axes used to correct baro for position error effects + 5.0 + 50.0 + m/s 1 - true modules/ekf2 @@ -1148,8 +1144,8 @@ Assumes measurement is timestamped at trailing edge of integration periodtrue modules/ekf2 - - Vision Position Estimator delay relative to IMU measurements + + Auxillary Velocity Estimate (e.g from a landing target) delay relative to IMU measurements 0 300 ms @@ -1157,303 +1153,267 @@ Assumes measurement is timestamped at trailing edge of integration periodtrue modules/ekf2 - - Integer bitmask controlling GPS checks - Set bits to 1 to enable checks. Checks enabled by the following bit positions 0 : Minimum required sat count set by EKF2_REQ_NSATS 1 : Minimum required GDoP set by EKF2_REQ_GDOP 2 : Maximum allowed horizontal position error set by EKF2_REQ_EPH 3 : Maximum allowed vertical position error set by EKF2_REQ_EPV 4 : Maximum allowed speed error set by EKF2_REQ_SACC 5 : Maximum allowed horizontal position rate set by EKF2_REQ_HDRIFT. This check can only be used if the vehicle is stationary during alignment. 6 : Maximum allowed vertical position rate set by EKF2_REQ_VDRIFT. This check can only be used if the vehicle is stationary during alignment. 7 : Maximum allowed horizontal speed set by EKF2_REQ_HDRIFT. This check can only be used if the vehicle is stationary during alignment. 8 : Maximum allowed vertical velocity discrepancy set by EKF2_REQ_VDRIFT + + Barometer measurement delay relative to IMU measurements 0 - 511 + 300 + ms + 1 + true modules/ekf2 - - Min sat count (EKF2_REQ_NSATS) - Min GDoP (EKF2_REQ_GDOP) - Max horizontal position error (EKF2_REQ_EPH) - Max vertical position error (EKF2_REQ_EPV) - Max speed error (EKF2_REQ_SACC) - Max horizontal position rate (EKF2_REQ_HDRIFT) - Max vertical position rate (EKF2_REQ_VDRIFT) - Max horizontal speed (EKF2_REQ_HDRIFT) - Max vertical velocity discrepancy (EKF2_REQ_VDRIFT) - - - Required EPH to use GPS - 2 - 100 - m + + Gate size for barometric and GPS height fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD 1 modules/ekf2 - - Required EPV to use GPS - 2 - 100 + + Measurement noise for barometric altitude + 0.01 + 15.0 m - 1 + 2 modules/ekf2 - - Required speed accuracy to use GPS - 0.5 - 5.0 - m/s - 2 + + X-axis ballistic coefficient used by the multi-rotor specific drag force model. +This should be adjusted to minimise variance of the X-axis drag specific force innovation sequence + 1.0 + 100.0 + kg/m**2 + 1 modules/ekf2 - - Required satellite count to use GPS - 4 - 12 + + Y-axis ballistic coefficient used by the multi-rotor specific drag force model. +This should be adjusted to minimise variance of the Y-axis drag specific force innovation sequence + 1.0 + 100.0 + kg/m**2 + 1 modules/ekf2 - - Required GDoP to use GPS - 1.5 - 5.0 + + Gate size for synthetic sideslip fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD 1 modules/ekf2 - - Maximum horizontal drift speed to use GPS + + Noise for synthetic sideslip fusion 0.1 1.0 m/s 2 modules/ekf2 - - Maximum vertical drift speed to use GPS - 0.1 - 1.5 - m/s + + Integer bitmask controlling handling of magnetic declination + Set bits in the following positions to enable functions. 0 : Set to true to use the declination from the geo_lookup library when the GPS position becomes available, set to false to always use the EKF2_MAG_DECL value. 1 : Set to true to save the EKF2_MAG_DECL parameter to the value returned by the EKF when the vehicle disarms. 2 : Set to true to always use the declination as an observation when 3-axis magnetometer fusion is being used. + 0 + 7 + true + modules/ekf2 + + use geo_lookup declination + save EKF2_MAG_DECL on disarm + use declination as an observation + + + + Specific drag force observation noise variance used by the multi-rotor specific drag force model. +Increasing it makes the multi-rotor wind estimates adjust more slowly + 0.5 + 10.0 + (m/sec**2)**2 2 modules/ekf2 - - Rate gyro noise for covariance prediction - 0.0001 - 0.1 - rad/s - 4 + + Measurement noise for airspeed fusion + 0.5 + 5.0 + m/s + 1 modules/ekf2 - - Accelerometer noise for covariance prediction + + Measurement noise for vision angle observations used when the vision system does not supply error estimates 0.01 - 1.0 - m/s/s + rad 2 modules/ekf2 - - Process noise for IMU rate gyro bias prediction - 0.0 - 0.01 - rad/s**2 - 6 + + Measurement noise for vision position observations used when the vision system does not supply error estimates + 0.01 + m + 2 modules/ekf2 - - Process noise for IMU accelerometer bias prediction - 0.0 - 0.01 - m/s**3 - 6 + + Vision Position Estimator delay relative to IMU measurements + 0 + 300 + ms + 1 + true modules/ekf2 - - Process noise for body magnetic field prediction - 0.0 - 0.1 - Gauss/s - 6 + + Gate size for vision estimate fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD + 1 modules/ekf2 - - Process noise for earth magnetic field prediction - 0.0 - 0.1 - Gauss/s - 6 + + X position of VI sensor focal point in body frame + m + 3 modules/ekf2 - - Process noise for wind velocity prediction - 0.0 - 1.0 - m/s/s - 3 - modules/ekf2 - - - Measurement noise for gps horizontal velocity - 0.01 - 5.0 - m/s - 2 - modules/ekf2 - - - Measurement noise for gps position - 0.01 - 10.0 - m - 2 - modules/ekf2 - - - Measurement noise for non-aiding position hold - 0.5 - 50.0 + + Y position of VI sensor focal point in body frame m - 1 + 3 modules/ekf2 - - Measurement noise for barometric altitude - 0.01 - 15.0 + + Z position of VI sensor focal point in body frame m - 2 - modules/ekf2 - - - Measurement noise for magnetic heading fusion - 0.01 - 1.0 - rad - 2 - modules/ekf2 - - - Measurement noise for magnetometer 3-axis fusion - 0.001 - 1.0 - Gauss 3 modules/ekf2 - - Measurement noise for airspeed fusion - 0.5 - 5.0 - m/s - 1 + + Boolean determining if synthetic sideslip measurements should fused + A value of 1 indicates that fusion is active Both sideslip fusion and airspeed fusion must be active for the EKF to continue navigating after loss of GPS. Use EKF2_ARSP_THR to activate airspeed fusion. + modules/ekf2 - - Noise for synthetic sideslip fusion - 0.1 - 1.0 - m/s + + 1-sigma IMU gyro switch-on bias + 0.0 + 0.2 + rad/sec 2 + true modules/ekf2 - - Magnetic declination - deg - 1 - modules/ekf2 - - - Gate size for magnetic heading fusion - 1.0 - SD - 1 - modules/ekf2 - - - Gate size for magnetometer XYZ component fusion - 1.0 - SD - 1 - modules/ekf2 - - - Integer bitmask controlling handling of magnetic declination - Set bits in the following positions to enable functions. 0 : Set to true to use the declination from the geo_lookup library when the GPS position becomes available, set to false to always use the EKF2_MAG_DECL value. 1 : Set to true to save the EKF2_MAG_DECL parameter to the value returned by the EKF when the vehicle disarms. 2 : Set to true to always use the declination as an observation when 3-axis magnetometer fusion is being used. + + Integer bitmask controlling GPS checks + Set bits to 1 to enable checks. Checks enabled by the following bit positions 0 : Minimum required sat count set by EKF2_REQ_NSATS 1 : Minimum required GDoP set by EKF2_REQ_GDOP 2 : Maximum allowed horizontal position error set by EKF2_REQ_EPH 3 : Maximum allowed vertical position error set by EKF2_REQ_EPV 4 : Maximum allowed speed error set by EKF2_REQ_SACC 5 : Maximum allowed horizontal position rate set by EKF2_REQ_HDRIFT. This check can only be used if the vehicle is stationary during alignment. 6 : Maximum allowed vertical position rate set by EKF2_REQ_VDRIFT. This check can only be used if the vehicle is stationary during alignment. 7 : Maximum allowed horizontal speed set by EKF2_REQ_HDRIFT. This check can only be used if the vehicle is stationary during alignment. 8 : Maximum allowed vertical velocity discrepancy set by EKF2_REQ_VDRIFT 0 - 7 - true + 511 modules/ekf2 - use geo_lookup declination - save EKF2_MAG_DECL on disarm - use declination as an observation + Min sat count (EKF2_REQ_NSATS) + Min GDoP (EKF2_REQ_GDOP) + Max horizontal position error (EKF2_REQ_EPH) + Max vertical position error (EKF2_REQ_EPV) + Max speed error (EKF2_REQ_SACC) + Max horizontal position rate (EKF2_REQ_HDRIFT) + Max vertical position rate (EKF2_REQ_VDRIFT) + Max horizontal speed (EKF2_REQ_HDRIFT) + Max vertical velocity discrepancy (EKF2_REQ_VDRIFT) - - Type of magnetometer fusion - Integer controlling the type of magnetometer fusion used - magnetic heading or 3-axis magnetometer. If set to automatic: heading fusion on-ground and 3-axis fusion in-flight with fallback to heading fusion if there is insufficient motion to make yaw or mag biases observable. + + GPS measurement delay relative to IMU measurements + 0 + 300 + ms + 1 true modules/ekf2 - - Magnetic heading - Automatic - None - 3-axis fusion - - - Horizontal acceleration threshold used by automatic selection of magnetometer fusion method. -This parameter is used when the magnetometer fusion method is set automatically (EKF2_MAG_TYPE = 0). If the filtered horizontal acceleration is greater than this parameter value, then the EKF will use 3-axis magnetomer fusion - 0.0 - 5.0 - m/s**2 - 2 + + X position of GPS antenna in body frame + m + 3 modules/ekf2 - - Yaw rate threshold used by automatic selection of magnetometer fusion method. -This parameter is used when the magnetometer fusion method is set automatically (EKF2_MAG_TYPE = 0). If the filtered yaw rate is greater than this parameter value, then the EKF will use 3-axis magnetomer fusion - 0.0 - 0.5 - rad/s - 2 + + Y position of GPS antenna in body frame + m + 3 modules/ekf2 - - Gate size for barometric height fusion - 1.0 - SD - 1 + + Z position of GPS antenna in body frame + m + 3 modules/ekf2 Gate size for GPS horizontal position fusion + Sets the number of standard deviations used by the innovation consistency test. 1.0 SD 1 modules/ekf2 + + Measurement noise for gps position + 0.01 + 10.0 + m + 2 + modules/ekf2 + Gate size for GPS velocity fusion + Sets the number of standard deviations used by the innovation consistency test. 1.0 SD 1 modules/ekf2 - - Gate size for TAS fusion + + Measurement noise for gps horizontal velocity + 0.01 + 5.0 + m/s + 2 + modules/ekf2 + + + Process noise for IMU rate gyro bias prediction + 0.0 + 0.01 + rad/s**2 + 6 + modules/ekf2 + + + Rate gyro noise for covariance prediction + 0.0001 + 0.1 + rad/s + 4 + modules/ekf2 + + + Gate size for magnetic heading fusion + Sets the number of standard deviations used by the innovation consistency test. 1.0 SD 1 modules/ekf2 - - Integer bitmask controlling data fusion and aiding methods - Set bits in the following positions to enable: 0 : Set to true to use GPS data if available 1 : Set to true to use optical flow data if available 2 : Set to true to inhibit IMU bias estimation 3 : Set to true to enable vision position fusion 4 : Set to true to enable vision yaw fusion 5 : Set to true to enable multi-rotor drag specific force fusion - 0 - 63 - true + + Measurement noise for magnetic heading fusion + 0.01 + 1.0 + rad + 2 modules/ekf2 - - use GPS - use optical flow - inhibit IMU bias estimation - vision position fusion - vision yaw fusion - multi-rotor drag fusion - Determines the primary source of height data used by the EKF @@ -1461,163 +1421,213 @@ This parameter is used when the magnetometer fusion method is set automatically true modules/ekf2 - GPS Barometric pressure - Vision + GPS Range sensor + Vision - - Measurement noise for range finder fusion - 0.01 + + X position of IMU in body frame m - 2 - modules/ekf2 - - - Range finder range dependant noise scaler - Specifies the increase in range finder noise with range. - 0.0 - 0.2 - m/m + 3 modules/ekf2 - - Gate size for range finder fusion - 1.0 - SD - 1 + + Y position of IMU in body frame + m + 3 modules/ekf2 - - Minimum valid range for the range finder - 0.01 + + Z position of IMU in body frame m - 2 + 3 modules/ekf2 - - Measurement noise for vision position observations used when the vision system does not supply error estimates - 0.01 - m - 2 + + ID of Magnetometer the learned bias is for + true modules/ekf2 - - Measurement noise for vision angle observations used when the vision system does not supply error estimates - 0.01 - rad - 2 + + Learned value of magnetometer X axis bias. +This is the amount of X-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated + -0.5 + 0.5 + mGauss + 3 + true modules/ekf2 - - Gate size for vision estimate fusion - 1.0 - SD - 1 + + Learned value of magnetometer Y axis bias. +This is the amount of Y-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated + -0.5 + 0.5 + mGauss + 3 + true modules/ekf2 - - Measurement noise for the optical flow sensor when it's reported quality metric is at the maximum - 0.05 - rad/s - 2 + + Learned value of magnetometer Z axis bias. +This is the amount of Z-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated + -0.5 + 0.5 + mGauss + 3 + true modules/ekf2 - - Measurement noise for the optical flow sensor - (when it's reported quality metric is at the minimum set by EKF2_OF_QMIN). The following condition must be met: EKF2_OF_N_MAXN >= EKF2_OF_N_MIN - 0.05 - rad/s + + Maximum fraction of learned mag bias saved at each disarm. +Smaller values make the saved mag bias learn slower from flight to flight. Larger values make it learn faster. Must be > 0.0 and <= 1.0 + 0.0 + 1.0 2 modules/ekf2 - - Optical Flow data will only be used if the sensor reports a quality metric >= EKF2_OF_QMIN - 0 - 255 + + State variance assumed for magnetometer bias storage. +This is a reference variance used to calculate the fraction of learned magnetometer bias that will be used to update the stored value. Smaller values will make the stored bias data adjust more slowly from flight to flight. Larger values will make it adjust faster + mGauss**2 + 8 + true modules/ekf2 - - Gate size for optical flow fusion - 1.0 - SD - 1 + + Horizontal acceleration threshold used by automatic selection of magnetometer fusion method. +This parameter is used when the magnetometer fusion method is set automatically (EKF2_MAG_TYPE = 0). If the filtered horizontal acceleration is greater than this parameter value, then the EKF will use 3-axis magnetomer fusion + 0.0 + 5.0 + m/s**2 + 2 modules/ekf2 - - Optical Flow data will not fused if the magnitude of the flow rate > EKF2_OF_RMAX - 1.0 - rad/s - 2 + + Process noise for body magnetic field prediction + 0.0 + 0.1 + Gauss/s + 6 modules/ekf2 - - Terrain altitude process noise - accounts for instability in vehicle height estimate - 0.5 - m/s + + Magnetic declination + deg 1 modules/ekf2 - - Magnitude of terrain gradient + + Magnetometer measurement delay relative to IMU measurements + 0 + 300 + ms + 1 + true + modules/ekf2 + + + Process noise for earth magnetic field prediction 0.0 - m/m - 2 + 0.1 + Gauss/s + 6 modules/ekf2 - - X position of IMU in body frame - m - 3 + + Gate size for magnetometer XYZ component fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD + 1 modules/ekf2 - - Y position of IMU in body frame - m + + Measurement noise for magnetometer 3-axis fusion + 0.001 + 1.0 + Gauss 3 modules/ekf2 - - Z position of IMU in body frame - m - 3 + + Type of magnetometer fusion + Integer controlling the type of magnetometer fusion used - magnetic heading or 3-axis magnetometer. If set to automatic: heading fusion on-ground and 3-axis fusion in-flight with fallback to heading fusion if there is insufficient motion to make yaw or mag biases observable. + true + modules/ekf2 + + Automatic + Magnetic heading + 3-axis fusion + None + + + + Yaw rate threshold used by automatic selection of magnetometer fusion method. +This parameter is used when the magnetometer fusion method is set automatically (EKF2_MAG_TYPE = 0). If the filtered yaw rate is greater than this parameter value, then the EKF will use 3-axis magnetomer fusion + 0.0 + 0.5 + rad/s + 2 modules/ekf2 - - X position of GPS antenna in body frame - m - 3 + + Minimum time of arrival delta between non-IMU observations before data is downsampled. +Baro and Magnetometer data will be averaged before downsampling, other data will be point sampled resulting in loss of information + 10 + 50 + ms + true modules/ekf2 - - Y position of GPS antenna in body frame + + Minimum valid range for the range finder + 0.01 m - 3 + 2 modules/ekf2 - - Z position of GPS antenna in body frame + + Measurement noise for non-aiding position hold + 0.5 + 50.0 m - 3 + 1 modules/ekf2 - - X position of range finder origin in body frame - m - 3 + + Optical flow measurement delay relative to IMU measurements +Assumes measurement is timestamped at trailing edge of integration period + 0 + 300 + ms + 1 + true modules/ekf2 - - Y position of range finder origin in body frame - m - 3 + + Gate size for optical flow fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD + 1 modules/ekf2 - - Z position of range finder origin in body frame - m - 3 + + Measurement noise for the optical flow sensor + (when it's reported quality metric is at the minimum set by EKF2_OF_QMIN). The following condition must be met: EKF2_OF_N_MAXN >= EKF2_OF_N_MIN + 0.05 + rad/s + 2 + modules/ekf2 + + + Measurement noise for the optical flow sensor when it's reported quality metric is at the maximum + 0.05 + rad/s + 2 modules/ekf2 @@ -1638,136 +1648,105 @@ This parameter is used when the magnetometer fusion method is set automatically 3 modules/ekf2 - - X position of VI sensor focal point in body frame - m - 3 + + Optical Flow data will only be used if the sensor reports a quality metric >= EKF2_OF_QMIN + 0 + 255 modules/ekf2 - - Y position of VI sensor focal point in body frame - m - 3 + + Optical Flow data will not fused if the magnitude of the flow rate > EKF2_OF_RMAX. +Control loops will be instructed to limit ground speed such that the flow rate produced by movement over ground is less than 50% of EKF2_OF_RMAX + 1.0 + rad/s + 2 modules/ekf2 - - Z position of VI sensor focal point in body frame - m - 3 + + Static pressure position error coefficient for the negative X axis. +This is the ratio of static pressure error to dynamic pressure generated by a negative wind relative velocity along the X body axis. +If the baro height estimate rises during backwards flight, then this will be a negative number + -0.5 + 0.5 + 2 modules/ekf2 - - Airspeed fusion threshold. A value of zero will deactivate airspeed fusion. Any other positive -value will determine the minimum airspeed which will still be fused - 0.0 - m/s - 1 + + Static pressure position error coefficient for the positive X axis +This is the ratio of static pressure error to dynamic pressure generated by a positive wind relative velocity along the X body axis. +If the baro height estimate rises during forward flight, then this will be a negative number + -0.5 + 0.5 + 2 modules/ekf2 - - Boolean determining if synthetic sideslip measurements should fused - A value of 1 indicates that fusion is active - + + Pressure position error coefficient for the Y axis. +This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Y body axis. +If the baro height estimate rises during sideways flight, then this will be a negative number + -0.5 + 0.5 + 2 modules/ekf2 - - Time constant of the velocity output prediction and smoothing filter - 1.0 - s - 2 - modules/ekf2 - - - Time constant of the position output prediction and smoothing filter. Controls how tightly the output track the EKF states - 0.1 - 1.0 - s - 2 - modules/ekf2 - - - 1-sigma IMU gyro switch-on bias - 0.0 - 0.2 - rad/sec - 2 - true - modules/ekf2 - - - 1-sigma IMU accelerometer switch-on bias - 0.0 + + Static pressure position error coefficient for the Z axis. +This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Z body axis + -0.5 0.5 - m/s/s 2 - true - modules/ekf2 - - - 1-sigma tilt angle uncertainty after gravity vector alignment - 0.0 - 0.5 - rad - 3 - true modules/ekf2 - - Range sensor pitch offset - -0.75 - 0.75 - rad - 3 + + Required EPH to use GPS + 2 + 100 + m + 1 modules/ekf2 - - Learned value of magnetometer X axis bias. -This is the amount of X-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated - -0.5 - 0.5 - mGauss - 3 - true + + Required EPV to use GPS + 2 + 100 + m + 1 modules/ekf2 - - Learned value of magnetometer Y axis bias. -This is the amount of Y-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated - -0.5 - 0.5 - mGauss - 3 - true + + Required GDoP to use GPS + 1.5 + 5.0 + 1 modules/ekf2 - - Learned value of magnetometer Z axis bias. -This is the amount of Z-axis magnetometer bias learned by the EKF and saved from the last flight. It must be set to zero if the ground based magnetometer calibration is repeated - -0.5 - 0.5 - mGauss - 3 - true + + Maximum horizontal drift speed to use GPS + 0.1 + 1.0 + m/s + 2 modules/ekf2 - - ID of Magnetometer the learned bias is for - true + + Required satellite count to use GPS + 4 + 12 modules/ekf2 - - State variance assumed for magnetometer bias storage. -This is a reference variance used to calculate the fraction of learned magnetometer bias that will be used to update the stored value. Smaller values will make the stored bias data adjust more slowly from flight to flight. Larger values will make it adjust faster - mGauss**2 - 8 - true + + Required speed accuracy to use GPS + 0.5 + 5.0 + m/s + 2 modules/ekf2 - - Maximum fraction of learned mag bias saved at each disarm. -Smaller values make the saved mag bias learn slower from flight to flight. Larger values make it learn faster. Must be > 0.0 and <= 1.0 - 0.0 - 1.0 + + Maximum vertical drift speed to use GPS + 0.1 + 1.5 + m/s 2 modules/ekf2 @@ -1776,17 +1755,10 @@ Smaller values make the saved mag bias learn slower from flight to flight. Large If this parameter is enabled then the estimator will make use of the range finder measurements to estimate it's height even if range sensor is not the primary height source. It will only do so if conditions for range measurement fusion are met. modules/ekf2 - Range aid enabled Range aid disabled + Range aid enabled - - Maximum horizontal velocity allowed for range aid mode - If the vehicle horizontal speed exceeds this value then the estimator will not fuse range measurements to estimate it's height. This only applies when range aid mode is activated (EKF2_RNG_AID = enabled). - 0.1 - 2 - modules/ekf2 - Maximum absolute altitude (height above ground level) allowed for range aid mode If the vehicle absolute altitude exceeds this value then the estimator will not fuse range measurements to estimate it's height. This only applies when range aid mode is activated (EKF2_RNG_AID = enabled). @@ -1802,154 +1774,231 @@ Smaller values make the saved mag bias learn slower from flight to flight. Large SD modules/ekf2 - - Specific drag force observation noise variance used by the multi-rotor specific drag force model. -Increasing it makes the multi-rotor wind estimates adjust more slowly - 0.5 - 10.0 - (m/sec**2)**2 - 2 + + Maximum horizontal velocity allowed for range aid mode + If the vehicle horizontal speed exceeds this value then the estimator will not fuse range measurements to estimate it's height. This only applies when range aid mode is activated (EKF2_RNG_AID = enabled). + 0.1 + 2 modules/ekf2 - - X-axis ballistic coefficient used by the multi-rotor specific drag force model. -This should be adjusted to minimise variance of the X-axis drag specific force innovation sequence - 1.0 - 100.0 - kg/m**2 + + Range finder measurement delay relative to IMU measurements + 0 + 300 + ms 1 + true modules/ekf2 - - Y-axis ballistic coefficient used by the multi-rotor specific drag force model. -This should be adjusted to minimise variance of the Y-axis drag specific force innovation sequence + + Gate size for range finder fusion + Sets the number of standard deviations used by the innovation consistency test. 1.0 - 100.0 - kg/m**2 - 1 - modules/ekf2 - - - Upper limit on airspeed along individual axes used to correct baro for position error effects - 5.0 - 50.0 - m/s + SD 1 modules/ekf2 - - Static pressure position error coefficient for the positive X axis -This is the ratio of static pressure error to dynamic pressure generated by a positive wind relative velocity along the X body axis. -If the baro height estimate rises during forward flight, then this will be a negative number - -0.5 - 0.5 + + Measurement noise for range finder fusion + 0.01 + m 2 modules/ekf2 - - Static pressure position error coefficient for the negative X axis. -This is the ratio of static pressure error to dynamic pressure generated by a negative wind relative velocity along the X body axis. -If the baro height estimate rises during backwards flight, then this will be a negative number - -0.5 - 0.5 - 2 + + Range sensor pitch offset + -0.75 + 0.75 + rad + 3 modules/ekf2 - - Pressure position error coefficient for the Y axis. -This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Y body axis. -If the baro height estimate rises during sideways flight, then this will be a negative number - -0.5 - 0.5 - 2 + + X position of range finder origin in body frame + m + 3 modules/ekf2 - - Static pressure position error coefficient for the Z axis. -This is the ratio of static pressure error to dynamic pressure generated by a wind relative velocity along the Z body axis - -0.5 - 0.5 - 2 + + Y position of range finder origin in body frame + m + 3 modules/ekf2 - - Accelerometer bias learning limit. The ekf delta velocity bias states will be limited to within a range equivalent to +- of this value - 0.0 - 0.8 - m/s/s - 2 + + Z position of range finder origin in body frame + m + 3 modules/ekf2 - - Maximum IMU accel magnitude that allows IMU bias learning. -If the magnitude of the IMU accelerometer vector exceeds this value, the EKF delta velocity state estimation will be inhibited. -This reduces the adverse effect of high manoeuvre accelerations and IMU nonlinerity and scale factor errors on the delta velocity bias estimates - 20.0 - 200.0 - m/s/s - 1 + + Range finder range dependant noise scaler + Specifies the increase in range finder noise with range. + 0.0 + 0.2 + m/m modules/ekf2 - - Maximum IMU gyro angular rate magnitude that allows IMU bias learning. -If the magnitude of the IMU angular rate vector exceeds this value, the EKF delta velocity state estimation will be inhibited. -This reduces the adverse effect of rapid rotation rates and associated errors on the delta velocity bias estimates - 2.0 - 20.0 - rad/s + + Gate size for TAS fusion + Sets the number of standard deviations used by the innovation consistency test. + 1.0 + SD 1 modules/ekf2 - - Time constant used by acceleration and angular rate magnitude checks used to inhibit delta velocity bias learning. -The vector magnitude of angular rate and acceleration used to check if learning should be inhibited has a peak hold filter applied to it with an exponential decay. -This parameter controls the time constant of the decay + + Time constant of the position output prediction and smoothing filter. Controls how tightly the output track the EKF states 0.1 1.0 s 2 modules/ekf2 - - - - Attitude Wheel Time Constant - This defines the latency between a steering step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. - 0.4 + + Time constant of the velocity output prediction and smoothing filter 1.0 s 2 - 0.05 - modules/gnd_att_control + modules/ekf2 - - Attitude Roll Time Constant - This defines the latency between a roll step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. - 0.4 + + Magnitude of terrain gradient + 0.0 + m/m + 2 + modules/ekf2 + + + Terrain altitude process noise - accounts for instability in vehicle height estimate + 0.5 + m/s + 1 + modules/ekf2 + + + Process noise for wind velocity prediction + 0.0 1.0 - s + m/s/s + 3 + modules/ekf2 + + + + + Acro body x max rate + This is the rate the controller is trying to achieve if the user applies full roll stick input in acro mode. + 45 + 720 + degrees + modules/fw_att_control + + + Acro body y max rate + This is the body y rate the controller is trying to achieve if the user applies full pitch stick input in acro mode. + 45 + 720 + degrees + modules/fw_att_control + + + Acro body z max rate + This is the body z rate the controller is trying to achieve if the user applies full yaw stick input in acro mode. + 10 + 180 + degrees + modules/fw_att_control + + + Airspeed mode + For small wings or VTOL without airspeed sensor this parameter can be used to enable flying without an airspeed reading + modules/fw_att_control + + Normal (use airspeed if available) + Airspeed disabled + + + + Whether to scale throttle by battery power level + This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The fixed wing should constantly behave as if it was fully charged with reduced max thrust at lower battery percentages. i.e. if cruise speed is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + modules/fw_att_control + + + Scale factor for flaperons + 0.0 + 1.0 + norm 2 - 0.05 + 0.01 modules/fw_att_control - - Attitude pitch time constant - This defines the latency between a pitch step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. - 0.2 + + Scale factor for flaps + 0.0 1.0 - s + norm 2 - 0.05 + 0.01 modules/fw_att_control - - Pitch rate proportional gain - This defines how much the elevator input will be commanded depending on the current body angular rate error. - 0.005 + + Max manual pitch + Max pitch for manual control in attitude stabilized mode + 0.0 + 90.0 + deg + 1 + 0.5 + modules/fw_att_control + + + Manual pitch scale + Scale factor applied to the desired pitch actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. + 0.0 + norm + 2 + 0.01 + modules/fw_att_control + + + Max manual roll + Max roll for manual control in attitude stabilized mode + 0.0 + 90.0 + deg + 1 + 0.5 + modules/fw_att_control + + + Manual roll scale + Scale factor applied to the desired roll actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. + 0.0 1.0 + norm + 2 + 0.01 + modules/fw_att_control + + + Manual yaw scale + Scale factor applied to the desired yaw actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. + 0.0 + norm + 2 + 0.01 + modules/fw_att_control + + + Pitch rate feed forward + Direct feed forward from rate setpoint to control surface output + 0.0 + 10.0 %/rad/s - 3 - 0.005 + 2 + 0.05 modules/fw_att_control @@ -1962,12 +2011,31 @@ This parameter controls the time constant of the decay 0.005 modules/fw_att_control - - Maximum positive / up pitch rate - This limits the maximum pitch up angular rate the controller will output (in degrees per second). Setting a value of zero disables the limit. + + Pitch rate integrator limit + The portion of the integrator part in the control surface deflection is limited to this value 0.0 + 1.0 + 2 + 0.05 + modules/fw_att_control + + + Pitch rate proportional gain + This defines how much the elevator input will be commanded depending on the current body angular rate error. + 0.005 + 1.0 + %/rad/s + 3 + 0.005 + modules/fw_att_control + + + Pitch setpoint offset + An airframe specific offset of the pitch setpoint in degrees, the value is added to the pitch setpoint and should correspond to the typical cruise speed of the airframe. + -90.0 90.0 - deg/s + deg 1 0.5 modules/fw_att_control @@ -1982,23 +2050,51 @@ This parameter controls the time constant of the decay 0.5 modules/fw_att_control - - Pitch rate integrator limit - The portion of the integrator part in the control surface deflection is limited to this value + + Maximum positive / up pitch rate + This limits the maximum pitch up angular rate the controller will output (in degrees per second). Setting a value of zero disables the limit. 0.0 + 90.0 + deg/s + 1 + 0.5 + modules/fw_att_control + + + Attitude pitch time constant + This defines the latency between a pitch step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. + 0.2 1.0 + s 2 0.05 modules/fw_att_control - - Roll rate proportional Gain - This defines how much the aileron input will be commanded depending on the current body angular rate error. - 0.005 + + Threshold for Rattitude mode + Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints + 0.0 1.0 + 2 + 0.01 + modules/fw_att_control + + + Roll control to yaw control feedforward gain + This gain can be used to counteract the "adverse yaw" effect for fixed wings. When the plane enters a roll it will tend to yaw the nose out of the turn. This gain enables the use of a yaw actuator (rudder, airbrakes, ...) to counteract this effect. + 0.0 + 1 + 0.01 + modules/fw_att_control + + + Roll rate feed forward + Direct feed forward from rate setpoint to control surface output. Use this to obtain a tigher response of the controller without introducing noise amplification. + 0.0 + 10.0 %/rad/s - 3 - 0.005 + 2 + 0.05 modules/fw_att_control @@ -2020,6 +2116,26 @@ This parameter controls the time constant of the decay 0.05 modules/fw_att_control + + Roll rate proportional Gain + This defines how much the aileron input will be commanded depending on the current body angular rate error. + 0.005 + 1.0 + %/rad/s + 3 + 0.005 + modules/fw_att_control + + + Roll setpoint offset + An airframe specific offset of the roll setpoint in degrees, the value is added to the roll setpoint and should correspond to the typical cruise speed of the airframe. + -90.0 + 90.0 + deg + 1 + 0.5 + modules/fw_att_control + Maximum roll rate This limits the maximum roll rate the controller will output (in degrees per second). Setting a value of zero disables the limit. @@ -2030,68 +2146,26 @@ This parameter controls the time constant of the decay 0.5 modules/fw_att_control - - Yaw rate proportional gain - This defines how much the rudder input will be commanded depending on the current body angular rate error. - 0.005 + + Attitude Roll Time Constant + This defines the latency between a roll step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. + 0.4 1.0 - %/rad/s - 3 - 0.005 + s + 2 + 0.05 modules/fw_att_control - - Yaw rate integrator gain - This gain defines how much control response will result out of a steady state error. It trims any constant error. - 0.0 - 50.0 - %/rad - 1 - 0.5 - modules/fw_att_control - - - Yaw rate integrator limit - The portion of the integrator part in the control surface deflection is limited to this value + + Wheel steering rate feed forward + Direct feed forward from rate setpoint to control surface output 0.0 - 1.0 + 10.0 + %/rad/s 2 0.05 modules/fw_att_control - - Maximum yaw rate - This limits the maximum yaw rate the controller will output (in degrees per second). Setting a value of zero disables the limit. - 0.0 - 90.0 - deg/s - 1 - 0.5 - modules/fw_att_control - - - Roll control to yaw control feedforward gain - This gain can be used to counteract the "adverse yaw" effect for fixed wings. When the plane enters a roll it will tend to yaw the nose out of the turn. This gain enables the use of a yaw actuator (rudder, airbrakes, ...) to counteract this effect. - 0.0 - 1 - 0.01 - modules/fw_att_control - - - Enable wheel steering controller - - modules/fw_att_control - - - Wheel steering rate proportional gain - This defines how much the wheel steering input will be commanded depending on the current body angular rate error. - 0.005 - 1.0 - %/rad/s - 3 - 0.005 - modules/fw_att_control - Wheel steering rate integrator gain This gain defines how much control response will result out of a steady state error. It trims any constant error. @@ -2111,6 +2185,21 @@ This parameter controls the time constant of the decay 0.05 modules/fw_att_control + + Wheel steering rate proportional gain + This defines how much the wheel steering input will be commanded depending on the current body angular rate error. + 0.005 + 1.0 + %/rad/s + 3 + 0.005 + modules/fw_att_control + + + Enable wheel steering controller + + modules/fw_att_control + Maximum wheel steering rate This limits the maximum wheel steering rate the controller will output (in degrees per second). Setting a value of zero disables the limit. @@ -2121,9 +2210,9 @@ This parameter controls the time constant of the decay 0.5 modules/fw_att_control - - Roll rate feed forward - Direct feed forward from rate setpoint to control surface output. Use this to obtain a tigher response of the controller without introducing noise amplification. + + Yaw rate feed forward + Direct feed forward from rate setpoint to control surface output 0.0 10.0 %/rad/s @@ -2131,226 +2220,168 @@ This parameter controls the time constant of the decay 0.05 modules/fw_att_control - - Pitch rate feed forward - Direct feed forward from rate setpoint to control surface output + + Yaw rate integrator gain + This gain defines how much control response will result out of a steady state error. It trims any constant error. 0.0 - 10.0 - %/rad/s - 2 - 0.05 + 50.0 + %/rad + 1 + 0.5 modules/fw_att_control - - Yaw rate feed forward - Direct feed forward from rate setpoint to control surface output + + Yaw rate integrator limit + The portion of the integrator part in the control surface deflection is limited to this value 0.0 - 10.0 - %/rad/s + 1.0 2 0.05 modules/fw_att_control - - Wheel steering rate feed forward - Direct feed forward from rate setpoint to control surface output - 0.0 - 10.0 + + Yaw rate proportional gain + This defines how much the rudder input will be commanded depending on the current body angular rate error. + 0.005 + 1.0 %/rad/s - 2 - 0.05 + 3 + 0.005 modules/fw_att_control - - Minimal speed for yaw coordination - For airspeeds above this value, the yaw rate is calculated for a coordinated turn. Set to a very high value to disable. + + Maximum yaw rate + This limits the maximum yaw rate the controller will output (in degrees per second). Setting a value of zero disables the limit. 0.0 - 1000.0 - m/s + 90.0 + deg/s 1 0.5 modules/fw_att_control - - Method used for yaw coordination - The param value sets the method used to calculate the yaw rate 0: open-loop zero lateral acceleration based on kinematic constraints 1: closed-loop: try to reduce lateral acceleration to 0 by measuring the acceleration - 0 - 1 - modules/fw_att_control - - closed-loop - open-loop - + + + + Climbout Altitude difference + If the altitude error exceeds this parameter, the system will climb out with maximum throttle and minimum airspeed until it is closer than this distance to the desired altitude. Mostly used for takeoff waypoints / modes. Set to 0 to disable climbout mode (not recommended). + 0.0 + 150.0 + m + 1 + 0.5 + modules/fw_pos_control_l1 - - Roll setpoint offset - An airframe specific offset of the roll setpoint in degrees, the value is added to the roll setpoint and should correspond to the typical cruise speed of the airframe. - -90.0 - 90.0 - deg + + L1 damping + Damping factor for L1 control. + 0.6 + 0.9 + 2 + 0.05 + modules/fw_pos_control_l1 + + + L1 period + This is the L1 distance and defines the tracking point ahead of the aircraft its following. A value of 18-25 meters works for most aircraft. Shorten slowly during tuning until response is sharp without oscillation. + 12.0 + 50.0 + m 1 0.5 - modules/fw_att_control + modules/fw_pos_control_l1 - - Pitch setpoint offset - An airframe specific offset of the pitch setpoint in degrees, the value is added to the pitch setpoint and should correspond to the typical cruise speed of the airframe. - -90.0 - 90.0 + + Min. airspeed scaling factor for landing + Multiplying this factor with the minimum airspeed of the plane gives the target airspeed the landing approach. FW_AIRSPD_MIN * FW_LND_AIRSPD_SC + 1.0 + 1.5 + norm + 2 + 0.01 + modules/fw_pos_control_l1 + + + Landing slope angle + 1.0 + 15.0 deg 1 0.5 - modules/fw_att_control + modules/fw_pos_control_l1 - - Max manual roll - Max roll for manual control in attitude stabilized mode + + Landing flare altitude (relative to landing altitude) 0.0 - 90.0 + 25.0 + m + 1 + 0.5 + modules/fw_pos_control_l1 + + + Flare, maximum pitch + Maximum pitch during flare, a positive sign means nose up Applied once FW_LND_TLALT is reached + 0 + 45.0 deg 1 0.5 - modules/fw_att_control + modules/fw_pos_control_l1 - - Max manual pitch - Max pitch for manual control in attitude stabilized mode - 0.0 - 90.0 + + Flare, minimum pitch + Minimum pitch during flare, a positive sign means nose up Applied once FW_LND_TLALT is reached + 0 + 15.0 deg 1 0.5 - modules/fw_att_control + modules/fw_pos_control_l1 - - Scale factor for flaps - 0.0 - 1.0 - norm - 2 - 0.01 - modules/fw_att_control + + Landing heading hold horizontal distance. +Set to 0 to disable heading hold + 0 + 30.0 + m + 1 + 0.5 + modules/fw_pos_control_l1 - - Scale factor for flaperons - 0.0 - 1.0 - norm - 2 - 0.01 - modules/fw_att_control + + FW_LND_HVIRT + 1.0 + 15.0 + m + 1 + 0.5 + modules/fw_pos_control_l1 - - Disable airspeed sensor - For small wings or VTOL without airspeed sensor this parameter can be used to enable flying without an airspeed reading - - modules/fw_att_control + + Landing throttle limit altitude (relative landing altitude) + Default of -1.0 lets the system default to applying throttle limiting at 2/3 of the flare altitude. + -1.0 + 30.0 + m + 1 + 0.5 + modules/fw_pos_control_l1 - - Manual roll scale - Scale factor applied to the desired roll actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. - 0.0 - 1.0 - norm - 2 - 0.01 - modules/fw_att_control - - - Manual pitch scale - Scale factor applied to the desired pitch actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. - 0.0 - norm - 2 - 0.01 - modules/fw_att_control - - - Manual yaw scale - Scale factor applied to the desired yaw actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. - 0.0 - norm - 2 - 0.01 - modules/fw_att_control - - - Whether to scale throttle by battery power level - This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The fixed wing should constantly behave as if it was fully charged with reduced max thrust at lower battery percentages. i.e. if cruise speed is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + Use terrain estimate during landing - modules/fw_att_control - - - Acro body x max rate - This is the rate the controller is trying to achieve if the user applies full roll stick input in acro mode. - 45 - 720 - degrees - modules/fw_att_control - - - Acro body y max rate - This is the body y rate the controller is trying to achieve if the user applies full pitch stick input in acro mode. - 45 - 720 - degrees - modules/fw_att_control - - - Acro body z max rate - This is the body z rate the controller is trying to achieve if the user applies full yaw stick input in acro mode. - 10 - 180 - degrees - modules/fw_att_control + modules/fw_pos_control_l1 - - Threshold for Rattitude mode - Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints + + Positive pitch limit + The maximum positive pitch the controller will output. 0.0 - 1.0 - 2 - 0.01 - modules/fw_att_control - - - - - L1 period - This is the L1 distance and defines the tracking point ahead of the aircraft its following. A value of 18-25 meters works for most aircraft. Shorten slowly during tuning until response is sharp without oscillation. - 12.0 - 50.0 - m + 60.0 + deg 1 0.5 modules/fw_pos_control_l1 - - L1 damping - Damping factor for L1 control. - 0.6 - 0.9 - 2 - 0.05 - modules/fw_pos_control_l1 - - - Cruise throttle - This is the throttle setting required to achieve the desired cruise speed. Most airframes have a value of 0.5-0.7. - 0.0 - 1.0 - norm - 2 - 0.01 - modules/fw_pos_control_l1 - - - Throttle max slew rate - Maximum slew rate for the commanded throttle - 0.0 - 1.0 - modules/fw_pos_control_l1 - Negative pitch limit The minimum negative pitch the controller will output. @@ -2361,16 +2392,6 @@ This parameter controls the time constant of the decay 0.5 modules/fw_pos_control_l1 - - Positive pitch limit - The maximum positive pitch the controller will output. - 0.0 - 60.0 - deg - 1 - 0.5 - modules/fw_pos_control_l1 - Controller roll limit The maximum roll the controller will output. @@ -2381,19 +2402,18 @@ This parameter controls the time constant of the decay 0.5 modules/fw_pos_control_l1 - - Throttle limit max - This is the maximum throttle % that can be used by the controller. For overpowered aircraft, this should be reduced to a value that provides sufficient thrust to climb at the maximum pitch angle PTCH_MAX. + + Scale throttle by pressure change + Automatically adjust throttle to account for decreased air density at higher altitudes. Start with a scale factor of 1.0 and adjust for different propulsion systems. When flying without airspeed sensor this will help to keep a constant performance over large altitude ranges. The default value of 0 will disable scaling. 0.0 - 1.0 - norm - 2 - 0.01 + 2.0 + 1 + 0.1 modules/fw_pos_control_l1 - - Throttle limit min - This is the minimum throttle % that can be used by the controller. For electric aircraft this will normally be set to zero, but can be set to a small non-zero value if a folding prop is fitted to prevent the prop from folding and unfolding repeatedly in-flight or to provide some aerodynamic drag from a turning prop to improve the descent rate. For aircraft with internal combustion engine this parameter should be set for desired idle rpm. + + Cruise throttle + This is the throttle setting required to achieve the desired cruise speed. Most airframes have a value of 0.5-0.7. 0.0 1.0 norm @@ -2421,104 +2441,39 @@ This parameter controls the time constant of the decay 0.01 modules/fw_pos_control_l1 - - Climbout Altitude difference - If the altitude error exceeds this parameter, the system will climb out with maximum throttle and minimum airspeed until it is closer than this distance to the desired altitude. Mostly used for takeoff waypoints / modes. Set to 0 to disable climbout mode (not recommended). + + Throttle limit max + This is the maximum throttle % that can be used by the controller. For overpowered aircraft, this should be reduced to a value that provides sufficient thrust to climb at the maximum pitch angle PTCH_MAX. 0.0 - 150.0 - m - 1 - 0.5 - modules/fw_pos_control_l1 - - - Landing slope angle - 1.0 - 15.0 - deg - 1 - 0.5 - modules/fw_pos_control_l1 - - - FW_LND_HVIRT - 1.0 - 15.0 - m - 1 - 0.5 + 1.0 + norm + 2 + 0.01 modules/fw_pos_control_l1 - - Landing flare altitude (relative to landing altitude) + + Throttle limit min + This is the minimum throttle % that can be used by the controller. For electric aircraft this will normally be set to zero, but can be set to a small non-zero value if a folding prop is fitted to prevent the prop from folding and unfolding repeatedly in-flight or to provide some aerodynamic drag from a turning prop to improve the descent rate. For aircraft with internal combustion engine this parameter should be set for desired idle rpm. 0.0 - 25.0 - m - 1 - 0.5 - modules/fw_pos_control_l1 - - - Landing throttle limit altitude (relative landing altitude) - Default of -1.0 lets the system default to applying throttle limiting at 2/3 of the flare altitude. - -1.0 - 30.0 - m - 1 - 0.5 - modules/fw_pos_control_l1 - - - Landing heading hold horizontal distance. -Set to 0 to disable heading hold - 0 - 30.0 - m - 1 - 0.5 - modules/fw_pos_control_l1 - - - Use terrain estimate during landing - - modules/fw_pos_control_l1 - - - Flare, minimum pitch - Minimum pitch during flare, a positive sign means nose up Applied once FW_LND_TLALT is reached - 0 - 15.0 - deg - 1 - 0.5 - modules/fw_pos_control_l1 - - - Flare, maximum pitch - Maximum pitch during flare, a positive sign means nose up Applied once FW_LND_TLALT is reached - 0 - 45.0 - deg - 1 - 0.5 - modules/fw_pos_control_l1 - - - Min. airspeed scaling factor for landing - Multiplying this factor with the minimum airspeed of the plane gives the target airspeed the landing approach. FW_AIRSPD_MIN * FW_LND_AIRSPD_SC - 1.0 - 1.5 + 1.0 norm 2 0.01 modules/fw_pos_control_l1 + + Throttle max slew rate + Maximum slew rate for the commanded throttle + 0.0 + 1.0 + modules/fw_pos_control_l1 + Launch detection - lib/launchdetection + modules/fw_pos_control_l1/launchdetection Catapult accelerometer threshold @@ -2527,17 +2482,7 @@ Set to 0 to disable heading hold m/s/s 1 0.5 - lib/launchdetection - - - Catapult time threshold - LAUN_CAT_A for LAUN_CAT_T serves as threshold to trigger launch detection. - 0.0 - 5.0 - s - 2 - 0.05 - lib/launchdetection + modules/fw_pos_control_l1/launchdetection Motor delay @@ -2547,7 +2492,7 @@ Set to 0 to disable heading hold s 1 0.5 - lib/launchdetection + modules/fw_pos_control_l1/launchdetection Maximum pitch before the throttle is powered up (during motor delay phase) @@ -2557,27 +2502,29 @@ Set to 0 to disable heading hold deg 1 0.5 - lib/launchdetection + modules/fw_pos_control_l1/launchdetection + + + Catapult time threshold + LAUN_CAT_A for LAUN_CAT_T serves as threshold to trigger launch detection. + 0.0 + 5.0 + s + 2 + 0.05 + modules/fw_pos_control_l1/launchdetection - - Trim ground speed + + Maximum Airspeed + If the airspeed is above this value, the TECS controller will try to decrease airspeed more aggressively. 0.0 40 m/s 1 0.5 - modules/gnd_pos_control - - - Maximum ground speed - 0.0 - 40 - m/s - 1 - 0.5 - modules/gnd_pos_control + modules/fw_pos_control_l1 Minimum Airspeed @@ -2589,16 +2536,6 @@ Set to 0 to disable heading hold 0.5 modules/fw_pos_control_l1 - - Maximum Airspeed - If the airspeed is above this value, the TECS controller will try to decrease airspeed more aggressively. - 0.0 - 40 - m/s - 1 - 0.5 - modules/fw_pos_control_l1 - Cruise Airspeed The fixed wing controller tries to fly at this airspeed. @@ -2619,12 +2556,55 @@ Set to 0 to disable heading hold 0.5 modules/fw_pos_control_l1 - - Minimum descent rate - This is the sink rate of the aircraft with the throttle set to THR_MIN and flown at the same airspeed as used to measure FW_T_CLMB_MAX. + + Complementary filter "omega" parameter for height + This is the cross-over frequency (in radians/second) of the complementary filter used to fuse vertical acceleration and barometric height to obtain an estimate of height rate and height. Increasing this frequency weights the solution more towards use of the barometer, whilst reducing it weights the solution more towards use of the accelerometer data. 1.0 - 5.0 - m/s + 10.0 + rad/s + 1 + 0.5 + modules/fw_pos_control_l1 + + + Height rate feed forward + 0.0 + 1.0 + 2 + 0.05 + modules/fw_pos_control_l1 + + + Height rate proportional factor + 0.0 + 1.0 + 2 + 0.05 + modules/fw_pos_control_l1 + + + Integrator gain + This is the integrator gain on the control loop. Increasing this gain increases the speed at which speed and height offsets are trimmed out, but reduces damping and increases overshoot. + 0.0 + 2.0 + 2 + 0.05 + modules/fw_pos_control_l1 + + + Pitch damping factor + This is the damping gain for the pitch demand loop. Increase to add damping to correct for oscillations in height. The default value of 0.0 will work well provided the pitch to servo controller has been tuned properly. + 0.0 + 2.0 + 1 + 0.1 + modules/fw_pos_control_l1 + + + Roll -> Throttle feedforward + Increasing this gain turn increases the amount of throttle that will be used to compensate for the additional drag created by turning. Ideally this should be set to approximately 10 x the extra sink rate in m/s created by a 45 degree bank turn. Increase this gain if the aircraft initially loses energy in turns and reduce if the aircraft initially gains energy in turns. Efficient high aspect-ratio aircraft (eg powered sailplanes) can use a lower value, whereas inefficient low aspect-ratio models (eg delta wings) can use a higher value. + 0.0 + 20.0 1 0.5 modules/fw_pos_control_l1 @@ -2639,16 +2619,43 @@ Set to 0 to disable heading hold 0.5 modules/fw_pos_control_l1 - - TECS time constant - This is the time constant of the TECS control algorithm (in seconds). Smaller values make it faster to respond, larger values make it slower to respond. + + Minimum descent rate + This is the sink rate of the aircraft with the throttle set to THR_MIN and flown at the same airspeed as used to measure FW_T_CLMB_MAX. + 1.0 + 5.0 + m/s + 1 + 0.5 + modules/fw_pos_control_l1 + + + Speed <--> Altitude priority + This parameter adjusts the amount of weighting that the pitch control applies to speed vs height errors. Setting it to 0.0 will cause the pitch control to control height and ignore speed errors. This will normally improve height accuracy but give larger airspeed errors. Setting it to 2.0 will cause the pitch control loop to control speed and ignore height errors. This will normally reduce airspeed errors, but give larger height errors. The default value of 1.0 allows the pitch control to simultaneously control height and speed. Note to Glider Pilots - set this parameter to 2.0 (The glider will adjust its pitch angle to maintain airspeed, ignoring changes in height). + 0.0 + 2.0 + 1 + 1.0 + modules/fw_pos_control_l1 + + + Complementary filter "omega" parameter for speed + This is the cross-over frequency (in radians/second) of the complementary filter used to fuse longitudinal acceleration and airspeed to obtain an improved airspeed estimate. Increasing this frequency weights the solution more towards use of the airspeed sensor, whilst reducing it weights the solution more towards use of the accelerometer data. 1.0 10.0 - s + rad/s 1 0.5 modules/fw_pos_control_l1 + + Speed rate P factor + 0.0 + 2.0 + 2 + 0.01 + modules/fw_pos_control_l1 + TECS Throttle time constant This is the time constant of the TECS throttle control algorithm (in seconds). Smaller values make it faster to respond, larger values make it slower to respond. @@ -2668,105 +2675,46 @@ Set to 0 to disable heading hold 0.1 modules/fw_pos_control_l1 - - Integrator gain - This is the integrator gain on the control loop. Increasing this gain increases the speed at which speed and height offsets are trimmed out, but reduces damping and increases overshoot. - 0.0 - 2.0 - 2 - 0.05 - modules/fw_pos_control_l1 - - - Maximum vertical acceleration - This is the maximum vertical acceleration (in m/s/s) either up or down that the controller will use to correct speed or height errors. The default value of 7 m/s/s (equivalent to +- 0.7 g) allows for reasonably aggressive pitch changes if required to recover from under-speed conditions. - 1.0 - 10.0 - m/s/s - 1 - 0.5 - modules/fw_pos_control_l1 - - - Complementary filter "omega" parameter for height - This is the cross-over frequency (in radians/second) of the complementary filter used to fuse vertical acceleration and barometric height to obtain an estimate of height rate and height. Increasing this frequency weights the solution more towards use of the barometer, whilst reducing it weights the solution more towards use of the accelerometer data. + + TECS time constant + This is the time constant of the TECS control algorithm (in seconds). Smaller values make it faster to respond, larger values make it slower to respond. 1.0 10.0 - rad/s + s 1 0.5 modules/fw_pos_control_l1 - - Complementary filter "omega" parameter for speed - This is the cross-over frequency (in radians/second) of the complementary filter used to fuse longitudinal acceleration and airspeed to obtain an improved airspeed estimate. Increasing this frequency weights the solution more towards use of the airspeed sensor, whilst reducing it weights the solution more towards use of the accelerometer data. + + Maximum vertical acceleration + This is the maximum vertical acceleration (in m/s/s) either up or down that the controller will use to correct speed or height errors. The default value of 7 m/s/s (equivalent to +- 0.7 g) allows for reasonably aggressive pitch changes if required to recover from under-speed conditions. 1.0 10.0 - rad/s + m/s/s 1 0.5 modules/fw_pos_control_l1 - - Roll -> Throttle feedforward - Increasing this gain turn increases the amount of throttle that will be used to compensate for the additional drag created by turning. Ideally this should be set to approximately 10 x the extra sink rate in m/s created by a 45 degree bank turn. Increase this gain if the aircraft initially loses energy in turns and reduce if the aircraft initially gains energy in turns. Efficient high aspect-ratio aircraft (eg powered sailplanes) can use a lower value, whereas inefficient low aspect-ratio models (eg delta wings) can use a higher value. + + Maximum ground speed 0.0 - 20.0 + 40 + m/s 1 0.5 - modules/fw_pos_control_l1 - - - Speed <--> Altitude priority - This parameter adjusts the amount of weighting that the pitch control applies to speed vs height errors. Setting it to 0.0 will cause the pitch control to control height and ignore speed errors. This will normally improve height accuracy but give larger airspeed errors. Setting it to 2.0 will cause the pitch control loop to control speed and ignore height errors. This will normally reduce airspeed errors, but give larger height errors. The default value of 1.0 allows the pitch control to simultaneously control height and speed. Note to Glider Pilots - set this parameter to 2.0 (The glider will adjust its pitch angle to maintain airspeed, ignoring changes in height). - 0.0 - 2.0 - 1 - 1.0 - modules/fw_pos_control_l1 + modules/gnd_pos_control - - Pitch damping factor - This is the damping gain for the pitch demand loop. Increase to add damping to correct for oscillations in height. The default value of 0.0 will work well provided the pitch to servo controller has been tuned properly. + + Trim ground speed 0.0 - 2.0 + 40 + m/s 1 - 0.1 - modules/fw_pos_control_l1 - - - Height rate proportional factor - 0.0 - 1.0 - 2 - 0.05 - modules/fw_pos_control_l1 - - - Height rate feed forward - 0.0 - 1.0 - 2 - 0.05 - modules/fw_pos_control_l1 - - - Speed rate P factor - 0.0 - 2.0 - 2 - 0.01 - modules/fw_pos_control_l1 + 0.5 + modules/gnd_pos_control - - Minimum follow target altitude - The minimum height in meters relative to home for following a target - 8.0 - meters - modules/navigator - Distance to follow target from The distance in meters to follow the target at @@ -2792,23 +2740,43 @@ but also ignore less noise 2 modules/navigator + + Minimum follow target altitude + The minimum height in meters relative to home for following a target + 8.0 + meters + modules/navigator + - - Control mode for speed - This allows the user to choose between closed loop gps speed or open loop cruise throttle speed - 0 - 1 - modules/gnd_pos_control - - close the loop with gps speed - open loop control - + + Whether to scale throttle by battery power level + This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The fixed wing should constantly behave as if it was fully charged with reduced max thrust at lower battery percentages. i.e. if cruise speed is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + modules/gnd_att_control - + + Groundspeed speed trim + This allows to scale the turning radius depending on the speed. + 0.0 + norm + 2 + 0.1 + modules/gnd_att_control + + + Manual yaw scale + Scale factor applied to the desired yaw actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. + 0.0 + norm + 2 + 0.01 + modules/gnd_att_control + + Speed proportional gain - This is the proportional gain for the speed closed loop controller - 0.005 + This is the derivative gain for the speed closed loop controller + 0.00 50.0 %m/s 3 @@ -2825,19 +2793,19 @@ but also ignore less noise 0.005 modules/gnd_pos_control - - Speed proportional gain - This is the derivative gain for the speed closed loop controller - 0.00 - 50.0 - %m/s + + Speed integral maximum value + This is the maxim value the integral can reach to prevent wind-up. + 0.005 + 50.0 + %m/s 3 0.005 modules/gnd_pos_control - - Speed integral maximum value - This is the maxim value the integral can reach to prevent wind-up. + + Speed proportional gain + This is the proportional gain for the speed closed loop controller 0.005 50.0 %m/s @@ -2855,30 +2823,41 @@ but also ignore less noise 0.005 modules/gnd_pos_control - - Wheel steering rate proportional gain - This defines how much the wheel steering input will be commanded depending on the current body angular rate error. - 0.005 - 1.0 - %/rad/s - 3 - 0.005 - modules/gnd_att_control + + Control mode for speed + This allows the user to choose between closed loop gps speed or open loop cruise throttle speed + 0 + 1 + modules/gnd_pos_control + + open loop control + close the loop with gps speed + - + Wheel steering rate integrator gain - This gain defines how much control response will result out of a steady state error. It trims any constant error. 0.00 - 0.5 + 30 %/rad 3 0.005 modules/gnd_att_control - + + Wheel steering rate feed forward + Direct feed forward from rate setpoint to control surface output + 0.0 + 10.0 + %/rad/s + 2 + 0.05 + modules/gnd_att_control + + Wheel steering rate integrator gain + This gain defines how much control response will result out of a steady state error. It trims any constant error. 0.00 - 30 + 0.5 %/rad 3 0.005 @@ -2893,6 +2872,26 @@ but also ignore less noise 0.05 modules/gnd_att_control + + Wheel steering rate proportional gain + This defines how much the wheel steering input will be commanded depending on the current body angular rate error. + 0.005 + 1.0 + %/rad/s + 3 + 0.005 + modules/gnd_att_control + + + Attitude Wheel Time Constant + This defines the latency between a steering step input and the achieved setpoint (inverse to a P gain). Half a second is a good start value and fits for most average systems. Smaller systems may require smaller values, but as this will wear out servos faster, the value should only be decreased as needed. + 0.4 + 1.0 + s + 2 + 0.05 + modules/gnd_att_control + Maximum wheel steering rate This limits the maximum wheel steering rate the controller will output (in degrees per second). Setting a value of zero disables the limit. @@ -2903,42 +2902,17 @@ but also ignore less noise 0.5 modules/gnd_att_control - - Wheel steering rate feed forward - Direct feed forward from rate setpoint to control surface output - 0.0 - 10.0 - %/rad/s + + + + L1 damping + Damping factor for L1 control. + 0.6 + 0.9 2 0.05 - modules/gnd_att_control - - - Manual yaw scale - Scale factor applied to the desired yaw actuator command in full manual mode. This parameter allows to adjust the throws of the control surfaces. - 0.0 - norm - 2 - 0.01 - modules/gnd_att_control - - - Groundspeed speed trim - This allows to scale the turning radius depending on the speed. - 0.0 - norm - 2 - 0.1 - modules/gnd_att_control - - - Whether to scale throttle by battery power level - This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The fixed wing should constantly behave as if it was fully charged with reduced max thrust at lower battery percentages. i.e. if cruise speed is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. - - modules/gnd_att_control + modules/gnd_pos_control - - L1 distance This is the waypoint radius @@ -2959,15 +2933,6 @@ but also ignore less noise 0.5 modules/gnd_pos_control - - L1 damping - Damping factor for L1 control. - 0.6 - 0.9 - 2 - 0.05 - modules/gnd_pos_control - Cruise throttle This is the throttle setting required to achieve the desired cruise speed. 10% is ok for a traxxas stampede vxl with ESC set to training mode @@ -2978,6 +2943,16 @@ but also ignore less noise 0.01 modules/gnd_pos_control + + Idle throttle + This is the minimum throttle while on the ground, it should be 0 for a rover + 0.0 + 0.4 + norm + 2 + 0.01 + modules/gnd_pos_control + Throttle limit max This is the maximum throttle % that can be used by the controller. For a Traxxas stampede vxl with the ESC set to training, 30 % is enough @@ -2998,16 +2973,6 @@ but also ignore less noise 0.01 modules/gnd_pos_control - - Idle throttle - This is the minimum throttle while on the ground, it should be 0 for a rover - 0.0 - 0.4 - norm - 2 - 0.01 - modules/gnd_pos_control - @@ -3017,8 +2982,8 @@ but also ignore less noise 1 drivers/gps - Enable Disable + Enable @@ -3029,11 +2994,11 @@ but also ignore less noise true drivers/gps - airborne with <4g acceleration stationary automotive - airborne with <2g acceleration airborne with <1g acceleration + airborne with <2g acceleration + airborne with <4g acceleration @@ -3048,20 +3013,20 @@ but also ignore less noise 1 modules/navigator - - Fixed bank angle - Roll in degrees during the loiter - 0.0 + + Fixed pitch angle + Pitch in degrees during the open loop loiter + -30.0 30.0 deg 1 0.5 modules/navigator - - Fixed pitch angle - Pitch in degrees during the open loop loiter - -30.0 + + Fixed bank angle + Roll in degrees during the loiter + 0.0 30.0 deg 1 @@ -3087,10 +3052,10 @@ but also ignore less noise 4 modules/navigator - Warning None - Return to Land + Warning Loiter + Return to Land Flight terminate @@ -3101,19 +3066,8 @@ but also ignore less noise 1 modules/navigator - AMSL WGS84 - - - - Geofence source - Select which position source should be used. Selecting GPS instead of global position makes sure that there is no dependence on the position estimator 0 = global position, 1 = GPS - 0 - 1 - modules/navigator - - GPS - GPOS + AMSL @@ -3142,6 +3096,17 @@ but also ignore less noise 1 modules/navigator + + Geofence source + Select which position source should be used. Selecting GPS instead of global position makes sure that there is no dependence on the position estimator 0 = global position, 1 = GPS + 0 + 1 + modules/navigator + + GPOS + GPS + + @@ -3149,31 +3114,55 @@ but also ignore less noise 0 300 s - drivers/iridiumsbd + drivers/telemetry/iridiumsbd - - Multicopter max climb rate - Maximum vertical velocity allowed in the landed state (m/s up and down) + + Airspeed max + Maximum airspeed allowed in the landed state (m/s) + 4 + 20 m/s 1 modules/land_detector - - Multicopter max horizontal velocity + + Fixedwing max short-term velocity + Maximum velocity integral in flight direction allowed in the landed state (m/s) + 2 + 10 + m/s + 1 + modules/land_detector + + + Fixedwing max horizontal velocity Maximum horizontal velocity allowed in the landed state (m/s) + 0.5 + 10 m/s 1 modules/land_detector - - Multicopter max rotation - Maximum allowed angular velocity around each axis allowed in the landed state. - deg/s + + Fixedwing max climb rate + Maximum vertical velocity allowed in the landed state (m/s up and down) + 5 + 20 + m/s 1 modules/land_detector + + Maximum altitude for multicopters + The system will obey this limit as a hard altitude limit. This setting will be consolidated with the GF_MAX_VER_DIST parameter. A negative value indicates no altitude limitation. + -1 + 10000 + m + 2 + modules/land_detector + Multicopter specific force threshold Multicopter threshold on the specific force measured by accelerometers in m/s^2 for free-fall detection @@ -3183,14 +3172,6 @@ but also ignore less noise 2 modules/land_detector - - Multicopter sub-hover throttle scaling - The range between throttle_min and throttle_hover is scaled by this parameter to define how close to minimum throttle the current throttle value needs to be in order to get accepted as landed. - 0.05 - 0.5 - 2 - modules/land_detector - Multicopter free-fall trigger time Seconds (decimal) that freefall conditions have to met before triggering a freefall. Minimal value is limited by LAND_DETECTOR_UPDATE_RATE=50Hz in landDetector.h @@ -3200,136 +3181,107 @@ but also ignore less noise 2 modules/land_detector - - Fixedwing max horizontal velocity - Maximum horizontal velocity allowed in the landed state (m/s) - 0.5 - 10 - m/s + + Multicopter max rotation + Maximum allowed angular velocity around each axis allowed in the landed state. + deg/s 1 modules/land_detector - - Fixedwing max climb rate - Maximum vertical velocity allowed in the landed state (m/s up and down) - 5 - 20 - m/s - 1 + + Multicopter sub-hover throttle scaling + The range between throttle_min and throttle_hover is scaled by this parameter to define how close to minimum throttle the current throttle value needs to be in order to get accepted as landed. + 0.05 + 0.5 + 2 modules/land_detector - - Fixedwing max short-term velocity - Maximum velocity integral in flight direction allowed in the landed state (m/s) - 2 - 10 + + Multicopter max horizontal velocity + Maximum horizontal velocity allowed in the landed state (m/s) m/s 1 modules/land_detector - - Airspeed max - Maximum airspeed allowed in the landed state (m/s) - 4 - 20 + + Multicopter max climb rate + Maximum vertical velocity allowed in the landed state (m/s up and down) m/s 1 modules/land_detector - + Total flight time in microseconds Total flight time of this autopilot. Higher 32 bits of the value. Flight time in microseconds = (LND_FLIGHT_T_HI << 32) | LND_FLIGHT_T_LO. 0 modules/land_detector - + Total flight time in microseconds Total flight time of this autopilot. Lower 32 bits of the value. Flight time in microseconds = (LND_FLIGHT_T_HI << 32) | LND_FLIGHT_T_LO. 0 modules/land_detector - - Maximum altitude for multicopters - The system will obey this limit as a hard altitude limit. This setting will be consolidated with the GF_MAX_VER_DIST parameter. A negative value indicates no altitude limitation. - -1 - 10000 - m - 2 - modules/land_detector - - - - Optical flow z offset from center - -1 - 1 - m - 3 - modules/local_position_estimator - - - Optical flow scale - 0.1 - 10.0 - m - 3 - modules/local_position_estimator - - - Optical flow rotation (roll/pitch) noise gain - 0.1 - 10.0 - m/s / (rad) - 3 - modules/local_position_estimator + + + Acceleration uncertainty + Variance of acceleration measurement used for landing target position prediction. Higher values results in tighter following of the measurements and more lenient outlier rejection + 0.01 + (m/s^2)^2 + 2 + modules/landing_target_estimator - - Optical flow angular velocity noise gain - 0.0 - 10.0 - m/s / (rad/s) - 3 - modules/local_position_estimator + + Landing target measurement uncertainty + Variance of the landing target measurement from the driver. Higher values results in less agressive following of the measurement and a smoother output as well as fewer rejected measurements. + tan(rad)^2 + 4 + modules/landing_target_estimator - - Optical flow minimum quality threshold + + Landing target mode + Configure the mode of the landing target. Depending on the mode, the landing target observations are used differently to aid position estimation. Mode Moving: The landing target may be moving around while in the field of view of the vehicle. Landing target measurements are not used to aid positioning. Mode Stationary: The landing target is stationary. Measured velocity w.r.t. the landing target is used to aid velocity estimation. 0 - 255 - 0 - modules/local_position_estimator - - - Sonar z standard deviation - 0.01 1 - m + modules/landing_target_estimator + + Moving + Stationary + + + + Initial landing target position uncertainty + Initial variance of the relative landing target position in x and y direction + 0.001 + m^2 3 - modules/local_position_estimator + modules/landing_target_estimator - - Sonar z offset from center of vehicle +down - -1 - 1 - m + + Scale factor for sensor measurements in sensor x axis + Landing target x measurements are scaled by this factor before being used + 0.01 3 - modules/local_position_estimator + modules/landing_target_estimator - - Lidar z standard deviation + + Scale factor for sensor measurements in sensor y axis + Landing target y measurements are scaled by this factor before being used 0.01 - 1 - m 3 - modules/local_position_estimator + modules/landing_target_estimator - - Lidar z offset from center of vehicle +down - -1 - 1 - m + + Initial landing target velocity uncertainty + Initial variance of the relative landing target velocity in x and y direction + 0.001 + (m/s)^2 3 - modules/local_position_estimator + modules/landing_target_estimator + + Accelerometer xy noise density Data sheet noise density = 150ug/sqrt(Hz) = 0.0015 m/s^2/sqrt(Hz) Larger than data sheet to account for tilt error. @@ -3356,27 +3308,98 @@ but also ignore less noise 2 modules/local_position_estimator - - GPS delay compensaton + + Max EPH allowed for GPS initialization + 1.0 + 5.0 + m + 3 + modules/local_position_estimator + + + Max EPV allowed for GPS initialization + 1.0 + 5.0 + m + 3 + modules/local_position_estimator + + + Enable publishing of a fake global position (e.g for AUTO missions using Optical Flow) +by initializing the estimator to the LPE_LAT/LON parameters when global information is unavailable 0 - 0.4 - sec - 2 + 1 modules/local_position_estimator - - Minimum GPS xy standard deviation, uses reported EPH if greater - 0.01 - 5 + + Flow gyro high pass filter cut off frequency + 0 + 2 + Hz + 3 + modules/local_position_estimator + + + Optical flow z offset from center + -1 + 1 m - 2 + 3 modules/local_position_estimator - - Minimum GPS z standard deviation, uses reported EPV if greater - 0.01 - 200 + + Optical flow minimum quality threshold + 0 + 255 + 0 + modules/local_position_estimator + + + Optical flow rotation (roll/pitch) noise gain + 0.1 + 10.0 + m/s / (rad) + 3 + modules/local_position_estimator + + + Optical flow angular velocity noise gain + 0.0 + 10.0 + m/s / (rad/s) + 3 + modules/local_position_estimator + + + Optical flow scale + 0.1 + 10.0 m + 3 + modules/local_position_estimator + + + Integer bitmask controlling data fusion + Set bits in the following positions to enable: 0 : Set to true to fuse GPS data if available, also requires GPS for altitude init 1 : Set to true to fuse optical flow data if available 2 : Set to true to fuse vision position 3 : Set to true to enable landing target 4 : Set to true to fuse land detector 5 : Set to true to publish AGL as local position down component 6 : Set to true to enable flow gyro compensation 7 : Set to true to enable baro fusion default (145 - GPS, baro, land detector) + 0 + 255 + modules/local_position_estimator + + fuse GPS, requires GPS for alt. init + fuse optical flow + fuse vision position + fuse landing target + fuse land detector + pub agl as lpos down + flow gyro compensation + fuse baro + + + + GPS delay compensaton + 0 + 0.4 + sec 2 modules/local_position_estimator @@ -3397,54 +3420,85 @@ EPV used if greater than this value 3 modules/local_position_estimator - - Max EPH allowed for GPS initialization - 1.0 - 5.0 + + Minimum GPS xy standard deviation, uses reported EPH if greater + 0.01 + 5 + m + 2 + modules/local_position_estimator + + + Minimum GPS z standard deviation, uses reported EPV if greater + 0.01 + 200 m + 2 + modules/local_position_estimator + + + Land detector xy velocity standard deviation + 0.01 + 10.0 + m/s 3 modules/local_position_estimator - - Max EPV allowed for GPS initialization - 1.0 - 5.0 + + Land detector z standard deviation + 0.001 + 10.0 m 3 modules/local_position_estimator - - Vision delay compensaton - Set to zero to enable automatic compensation from measurement timestamps - 0 - 0.1 - sec - 2 + + Local origin latitude for nav w/o GPS + -90 + 90 + deg + 8 modules/local_position_estimator - - Vision xy standard deviation - 0.01 + + Lidar z offset from center of vehicle +down + -1 1 m 3 modules/local_position_estimator - - Vision z standard deviation + + Lidar z standard deviation 0.01 - 100 + 1 m 3 modules/local_position_estimator - - Vicon position standard deviation - 0.0001 - 1 - m - 4 - modules/local_position_estimator + + Local origin longitude for nav w/o GPS + -180 + 180 + deg + 8 + modules/local_position_estimator + + + Minimum landing target standard covariance, uses reported covariance if greater + 0.0 + 10 + m^2 + 2 + modules/local_position_estimator + + + Accel bias propagation noise density + 0 + 1 + (m/s^2)/s/sqrt(Hz) + 8 + modules/local_position_estimator Position propagation noise density @@ -3455,6 +3509,14 @@ EPV used if greater than this value 8 modules/local_position_estimator + + Terrain random walk noise density, hilly/outdoor (0.1), flat/Indoor (0.001) + 0 + 1 + (m/s)/(sqrt(hz)) + 3 + modules/local_position_estimator + Velocity propagation noise density Increase to trust measurements more. Decrease to trust model more. @@ -3464,19 +3526,19 @@ EPV used if greater than this value 8 modules/local_position_estimator - - Accel bias propagation noise density - 0 + + Sonar z offset from center of vehicle +down + -1 1 - (m/s^2)/s/sqrt(Hz) - 8 + m + 3 modules/local_position_estimator - - Terrain random walk noise density, hilly/outdoor (0.1), flat/Indoor (0.001) - 0 + + Sonar z standard deviation + 0.01 1 - (m/s)/(sqrt(hz)) + m 3 modules/local_position_estimator @@ -3489,35 +3551,45 @@ Used to calculate increased terrain random walk nosie due to movement3 modules/local_position_estimator - - Flow gyro high pass filter cut off frequency - 0 - 2 - Hz - 3 + + Vicon position standard deviation + 0.0001 + 1 + m + 4 modules/local_position_estimator - - Enable publishing of a fake global position (e.g for AUTO missions using Optical Flow) -by initializing the estimator to the LPE_LAT/LON parameters when global information is unavailable + + Vision delay compensaton + Set to zero to enable automatic compensation from measurement timestamps 0 + 0.1 + sec + 2 + modules/local_position_estimator + + + Vision xy standard deviation + 0.01 1 + m + 3 modules/local_position_estimator - - Local origin latitude for nav w/o GPS - -90 - 90 - deg - 8 + + Vision z standard deviation + 0.01 + 100 + m + 3 modules/local_position_estimator - - Local origin longitude for nav w/o GPS - -180 - 180 - deg - 8 + + Required velocity xy standard deviation to publish position + 0.01 + 1.0 + m/s + 3 modules/local_position_estimator @@ -3528,14 +3600,6 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 0 modules/local_position_estimator - - Required velocity xy standard deviation to publish position - 0.01 - 1.0 - m/s - 3 - modules/local_position_estimator - Required z standard deviation to publish altitude/ terrain 0.3 @@ -3544,47 +3608,16 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 1 modules/local_position_estimator - - Land detector z standard deviation - 0.001 - 10.0 - m - 3 - modules/local_position_estimator - - - Land detector xy velocity standard deviation - 0.01 - 10.0 - m/s - 3 - modules/local_position_estimator - - - Integer bitmask controlling data fusion - Set bits in the following positions to enable: 0 : Set to true to fuse GPS data if available, also requires GPS for altitude init 1 : Set to true to fuse optical flow data if available 2 : Set to true to fuse vision position 3 : Set to true to fuse vision yaw 4 : Set to true to fuse land detector 5 : Set to true to publish AGL as local position down component 6 : Set to true to enable flow gyro compensation 7 : Set to true to enable baro fusion default (145 - GPS only) - 0 - 255 - modules/local_position_estimator - - fuse GPS, requires GPS for alt. init - fuse optical flow - fuse vision position - fuse vision yaw - fuse land detector - pub agl as lpos down - flow gyro compensation - fuse baro - - - - MAVLink system ID - 1 - 250 - true + + Broadcast heartbeats on local network + This allows a ground control station to automatically find the drone on the local network. modules/mavlink + + Never broadcast + Always broadcast + MAVLink component ID @@ -3593,12 +3626,18 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat true modules/mavlink + + Forward external setpoint messages + If set to 1 incoming external setpoint messages will be directly forwarded to the controllers if in offboard control mode + + modules/mavlink + MAVLink protocol version modules/mavlink - Always use version 1 Default to 1, switch to 2 if GCS sends version 2 + Always use version 1 Always use version 2 @@ -3609,10 +3648,48 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 240 modules/mavlink + + MAVLink system ID + 1 + 250 + true + modules/mavlink + MAVLink airframe type 1 + 27 modules/mavlink + + Generic micro air vehicle + Fixed wing aircraft + Quadrotor + Coaxial helicopter + Normal helicopter with tail rotor + Ground installation + Operator control unit / ground control station + Airship, controlled + Free balloon, uncontrolled + Rocket + Ground rover + Surface vessel, boat, ship + Submarine + Hexarotor + Octorotor + Tricopter + Flapping wing + Kite + Onboard companion controller + Two-rotor VTOL using control surfaces in vertical operation in addition. Tailsitter. + Quad-rotor VTOL using a V-shaped quad config in vertical operation. Tailsitter. + Tiltrotor VTOL + VTOL reserved 2 + VTOL reserved 3 + VTOL reserved 4 + VTOL reserved 5 + Onboard gimbal + Onboard ADSB peripheral + Use/Accept HIL GPS message even if not in HIL mode @@ -3620,28 +3697,6 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat modules/mavlink - - Forward external setpoint messages - If set to 1 incoming external setpoint messages will be directly forwarded to the controllers if in offboard control mode - - modules/mavlink - - - Broadcast heartbeats on local network - This allows a ground control station to automatically find the drone on the local network. - modules/mavlink - - Always broadcast - Never broadcast - - - - Test parameter - This parameter is not actively used by the system. Its purpose is to allow testing the parameter interface on the communication level. - -1000 - 1000 - modules/mavlink - @@ -3650,51 +3705,112 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat drivers/mkblctrl + + + Low pass filter frequency for Accelerometer + platforms/qurt/fc_addon/mpu_spi + + MPU9X50_ACC_LPF_460HZ + MPU9X50_ACC_LPF_184HZ + MPU9X50_ACC_LPF_92HZ + MPU9X50_ACC_LPF_41HZ + MPU9X50_ACC_LPF_20HZ + MPU9X50_ACC_LPF_10HZ + MPU9X50_ACC_LPF_5HZ + MPU9X50_ACC_LPF_460HZ_NOLPF + + + + Low pass filter frequency for Gyro + platforms/qurt/fc_addon/mpu_spi + + MPU9X50_GYRO_LPF_250HZ + MPU9X50_GYRO_LPF_184HZ + MPU9X50_GYRO_LPF_92HZ + MPU9X50_GYRO_LPF_41HZ + MPU9X50_GYRO_LPF_20HZ + MPU9X50_GYRO_LPF_10HZ + MPU9X50_GYRO_LPF_5HZ + MPU9X50_GYRO_LPF_3600HZ_NOLPF + + + + Sample rate in Hz + platforms/qurt/fc_addon/mpu_spi + + MPU9x50_SAMPLE_RATE_100HZ + MPU9x50_SAMPLE_RATE_200HZ + MPU9x50_SAMPLE_RATE_500HZ + MPU9x50_SAMPLE_RATE_1000HZ + + + - - Enable weather-vane mode takeoff for missions - - modules/vtol_att_control + + Set offboard loss failsafe mode + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander + + Land at current position + Loiter + Return to Land + - - Weather-vane mode for loiter - - modules/vtol_att_control + + Set offboard loss failsafe mode when RC is available + The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. + modules/commander + + Position control + Altitude control + Manual + Return to Land + Land at current position + Loiter + - - Weather-vane mode landings for missions - - modules/vtol_att_control + + Position control navigation loss response + This sets the flight mode that will be used if navigation accuracy is no longer adequte for position control. Navigation accuracy checks can be disabled using the CBRK_VELPOSERR parameter, but doing so will remove protection for all flight modes. + modules/commander + + Assume use of remote control after fallback. Switch to ALTCTL if a height estimate is available, else switch to MANUAL. + Assume no use of remote control after fallback. Switch to DESCEND if a height estimate is available, else switch to TERMINATION. + - - Take-off altitude - This is the minimum altitude the system will take off to. + + Action after TAKEOFF has been accepted + The mode transition after TAKEOFF has completed successfully. + modules/commander + + Hold + Mission (if valid) + + + + Altitude setpoint mode + 0: the system will follow a zero order hold altitude setpoint 1: the system will follow a first order hold altitude setpoint values follow the definition in enum mission_altitude_mode 0 - 80 + 1 + modules/navigator + + Zero Order Hold + First Order Hold + + + + Maximal horizontal distance from home to first waypoint + Failsafe check to prevent running mission stored from previous flight at a new takeoff location. Set a value of zero or less to disable. The mission will not be started if the current waypoint is more distant than MIS_DIS_1WP from the home position. + 0 + 10000 m 1 - 0.5 + 100 modules/navigator - - Minimum Loiter altitude - This is the minimum altitude the system will always obey. The intent is to stay out of ground effect. set to -1, if there shouldn't be a minimum loiter altitude - -1 - 80 - m - 1 - 0.5 - modules/navigator - - - Persistent onboard mission storage - When enabled, missions that have been uploaded by the GCS are stored and reloaded after reboot persistently. - - modules/navigator - - - Maximal horizontal distance from home to first waypoint - Failsafe check to prevent running mission stored from previous flight at a new takeoff location. Set a value of zero or less to disable. The mission will not be started if the current waypoint is more distant than MIS_DIS_1WP from the home position. + + Maximal horizontal distance between waypoint + Failsafe check to prevent running missions which are way too big. Set a value of zero or less to disable. The mission will not be started if any distance between two subsequent waypoints is greater than MIS_DIST_WPS. 0 10000 m @@ -3702,27 +3818,37 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 100 modules/navigator - - Maximal horizontal distance between waypoint - Failsafe check to prevent running missions which are way too big. Set a value of zero or less to disable. The mission will not be started if any distance between two subsequent waypoints is greater than MIS_DIST_WPS. - 0 - 10000 + + Minimum Loiter altitude + This is the minimum altitude the system will always obey. The intent is to stay out of ground effect. set to -1, if there shouldn't be a minimum loiter altitude + -1 + 80 m 1 - 100 + 0.5 modules/navigator - - Altitude setpoint mode - 0: the system will follow a zero order hold altitude setpoint 1: the system will follow a first order hold altitude setpoint values follow the definition in enum mission_altitude_mode + + Enable yaw control of the mount. (Only affects multicopters and ROI mission items) + If enabled, yaw commands will be sent to the mount and the vehicle will follow its heading mode as specified by MIS_YAWMODE. If disabled, the vehicle will yaw towards the ROI. 0 1 modules/navigator - First Order Hold - Zero Order Hold + Disable + Enable + + Take-off altitude + This is the minimum altitude the system will take off to. + 0 + 80 + m + 1 + 0.5 + modules/navigator + Multirotor only. Yaw setpoint mode The values are defined in the enum mission_altitude_mode @@ -3730,23 +3856,12 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 3 modules/navigator - Heading towards waypoint Heading as set by waypoint - Heading away from home + Heading towards waypoint Heading towards home - Heading towards ROI + Heading away from home - - Time in seconds we wait on reaching target heading at a waypoint if it is forced - If set > 0 it will ignore the target heading for normal waypoint acceptance. If the waypoint forces the heading the timeout will matter. For example on VTOL forwards transition. Mainly useful for VTOLs that have less yaw authority and might not reach target yaw in wind. Disabled by default. - -1 - 20 - s - 1 - 1 - modules/navigator - Max yaw error in degrees needed for waypoint heading acceptance 0 @@ -3756,14 +3871,14 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 1 modules/navigator - - Loiter radius (FW only) - Default value of loiter radius for missions, loiter, RTL, etc. (fixedwing only). - 25 - 1000 - m + + Time in seconds we wait on reaching target heading at a waypoint if it is forced + If set > 0 it will ignore the target heading for normal waypoint acceptance. If the waypoint forces the heading the timeout will matter. For example on VTOL forwards transition. Mainly useful for VTOLs that have less yaw authority and might not reach target yaw in wind. Disabled by default. + -1 + 20 + s 1 - 0.5 + 1 modules/navigator @@ -3776,6 +3891,25 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 0.5 modules/navigator + + Set data link loss failsafe mode + The data link loss failsafe will only be entered after a timeout, set by COM_DL_LOSS_T in seconds. Once the timeout occurs the selected action will be executed. Setting this parameter to 4 will enable CASA Outback Challenge rules, which are only recommended to participants of that competition. + modules/navigator + + Disabled + Loiter + Return to Land + Land at current position + Data Link Auto Recovery (CASA Outback Challenge rules) + Terminate + Lockdown + + + + Force VTOL mode takeoff and land + + modules/navigator + FW Altitude Acceptance Radius Acceptance radius for fixedwing altitude. @@ -3786,6 +3920,16 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 0.5 modules/navigator + + Loiter radius (FW only) + Default value of loiter radius for missions, loiter, RTL, etc. (fixedwing only). + 25 + 1000 + m + 1 + 0.5 + modules/navigator + MC Altitude Acceptance Radius Acceptance radius for multicopter altitude. @@ -3796,84 +3940,115 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 0.5 modules/navigator - - Set data link loss failsafe mode - The data link loss failsafe will only be entered after a timeout, set by COM_DL_LOSS_T in seconds. Once the timeout occurs the selected action will be executed. Setting this parameter to 4 will enable CASA Outback Challenge rules, which are only recommended to participants of that competition. - modules/navigator - - Loiter - Disabled - Land at current position - Return to Land - Terminate - Data Link Auto Recovery (CASA Outback Challenge rules) - Lockdown - - Set RC loss failsafe mode The RC loss failsafe will only be entered after a timeout, set by COM_RC_LOSS_T in seconds. If RC input checks have been disabled by setting the COM_RC_IN_MODE param it will not be triggered. Setting this parameter to 4 will enable CASA Outback Challenge rules, which are only recommended to participants of that competition. modules/navigator - Loiter Disabled - Land at current position + Loiter Return to Land - Terminate + Land at current position RC Auto Recovery (CASA Outback Challenge rules) + Terminate Lockdown + + RC Loss Loiter Time (CASA Outback Challenge rules) + The amount of time in seconds the system should loiter at current position before termination. Only applies if NAV_RCL_ACT is set to 2 (CASA Outback Challenge rules). Set to -1 to make the system skip loitering. + -1.0 + s + 1 + 0.1 + modules/navigator + Set traffic avoidance mode Enabling this will allow the system to respond to transponder data from e.g. ADSB transponders modules/navigator - Warn only Disabled - Land immediately + Warn only Return to Land + Land immediately - - Force VTOL mode takeoff and land + + Weather-vane mode landings for missions - modules/navigator + modules/vtol_att_control - - Set offboard loss failsafe mode - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander + + Weather-vane mode for loiter + + modules/vtol_att_control + + + Enable weather-vane mode takeoff for missions + + modules/vtol_att_control + + + + + Stabilize the mount (set to true for servo gimbal, false for passthrough). +Does not affect MAVLINK_ROI input + + drivers/vmount + + + Auxiliary channel to control pitch (in AUX input or manual mode) + 0 + 5 + drivers/vmount - Loiter - Land at current position - Return to Land + Disable + AUX1 + AUX2 + AUX3 + AUX4 + AUX5 - - Set offboard loss failsafe mode when RC is available - The offboard loss failsafe will only be entered after a timeout, set by COM_OF_LOSS_T in seconds. - modules/commander + + Auxiliary channel to control roll (in AUX input or manual mode) + 0 + 5 + drivers/vmount - Altitude control - Position control - Return to Land - Manual - Loiter - Land at current position + Disable + AUX1 + AUX2 + AUX3 + AUX4 + AUX5 - - Position control navigation loss response - This sets the flight mode that will be used if navigation accuracy is no longer adequte for position control. Navigation accuracy checks can be disabled using the CBRK_VELPOSERR parameter, but doing so will remove protection for all flight modes. - modules/commander + + Auxiliary channel to control yaw (in AUX input or manual mode) + 0 + 5 + drivers/vmount - Assume no use of remote control after fallback. Switch to DESCEND if a height estimate is available, else switch to TERMINATION. - Assume use of remote control after fallback. Switch to ALTCTL if a height estimate is available, else switch to MANUAL. + Disable + AUX1 + AUX2 + AUX3 + AUX4 + AUX5 - - + + Mavlink Component ID of the mount + If MNT_MODE_OUT is MAVLINK, mount configure/control commands will be sent with this component ID. + drivers/vmount + + + Mavlink System ID of the mount + If MNT_MODE_OUT is MAVLINK, mount configure/control commands will be sent with this target ID. + drivers/vmount + Mount input mode RC uses the AUX input channels (see MNT_MAN_* parameters), MAVLINK_ROI uses the MAV_CMD_DO_SET_ROI Mavlink message, and MAVLINK_DO_MOUNT the MAV_CMD_DO_MOUNT_CONFIGURE and MAV_CMD_DO_MOUNT_CONTROL messages to control a mount. @@ -3882,11 +4057,11 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat true drivers/vmount - RC + DISABLED AUTO - MAVLINK_DO_MOUNT + RC MAVLINK_ROI - DISABLED + MAVLINK_DO_MOUNT @@ -3896,18 +4071,16 @@ by initializing the estimator to the LPE_LAT/LON parameters when global informat 1 drivers/vmount - MAVLINK AUX + MAVLINK - - Mavlink System ID of the mount - If MNT_MODE_OUT is MAVLINK, mount configure/control commands will be sent with this target ID. - drivers/vmount - - - Mavlink Component ID of the mount - If MNT_MODE_OUT is MAVLINK, mount configure/control commands will be sent with this component ID. + + Mixer value for selecting a locking mode +if required for the gimbal (only in AUX output mode) + -1.0 + 1.0 + 3 drivers/vmount @@ -3918,60 +4091,25 @@ if required by the gimbal (only in AUX output mode) 3 drivers/vmount - - Mixer value for selecting a locking mode -if required for the gimbal (only in AUX output mode) - -1.0 - 1.0 - 3 - drivers/vmount - - - Auxiliary channel to control roll (in AUX input or manual mode) - 0 - 5 - drivers/vmount - - AUX1 - Disable - AUX3 - AUX2 - AUX5 - AUX4 - - - - Auxiliary channel to control pitch (in AUX input or manual mode) - 0 - 5 + + Offset for pitch channel output in degrees + -360.0 + 360.0 + 1 drivers/vmount - - AUX1 - Disable - AUX3 - AUX2 - AUX5 - AUX4 - - - Auxiliary channel to control yaw (in AUX input or manual mode) - 0 - 5 + + Offset for roll channel output in degrees + -360.0 + 360.0 + 1 drivers/vmount - - AUX1 - Disable - AUX3 - AUX2 - AUX5 - AUX4 - - - Stabilize the mount (set to true for servo gimbal, false for passthrough). -Does not affect MAVLINK_ROI input - + + Offset for yaw channel output in degrees + -360.0 + 360.0 + 1 drivers/vmount @@ -3995,166 +4133,122 @@ Does not affect MAVLINK_ROI input 1 drivers/vmount - - Offset for pitch channel output in degrees - -360.0 - 360.0 - 1 - drivers/vmount - - - Offset for roll channel output in degrees - -360.0 - 360.0 - 1 - drivers/vmount - - - Offset for yaw channel output in degrees - -360.0 - 360.0 - 1 - drivers/vmount - - - Max manual roll - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform - - - Max manual pitch - 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform + + Acro Expo factor +applied to input of all axis: roll, pitch, yaw + 0 Purely linear input curve 1 Purely cubic input curve + 0 + 1 + 2 + modules/mc_att_control - - Max manual yaw rate + + Max acro pitch rate +default: 2 turns per second 0.0 + 1000.0 deg/s - examples/mc_pos_control_multiplatform - - - Roll P gain - Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - examples/mc_att_control_multiplatform - - - Roll rate P gain - Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform + 1 + 5 + modules/mc_att_control - - Roll rate I gain - Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. + + Max acro roll rate +default: 2 turns per second 0.0 - examples/mc_att_control_multiplatform + 1000.0 + deg/s + 1 + 5 + modules/mc_att_control - - Roll rate D gain - Roll rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform + + Acro SuperExpo factor +applied to input of all axis: roll, pitch, yaw + 0 Pure Expo function 0.7 resonable shape enhancement for intuitive stick feel 0.95 very strong bent input curve only near maxima have effect + 0 + 0.95 + 2 + modules/mc_att_control - - Pitch P gain - Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. + + Max acro yaw rate +default 1.5 turns per second 0.0 - 1/s - examples/mc_att_control_multiplatform + 1000.0 + deg/s + 1 + 5 + modules/mc_att_control - - Pitch rate P gain - Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform + + Battery power level scaler + This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The copter should constantly behave as if it was fully charged with reduced max acceleration at lower battery percentages. i.e. if hover is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. + + modules/mc_att_control - - Pitch rate I gain - Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform + + Cutoff frequency for the low pass filter on the D-term in the rate controller + The D-term uses the derivative of the rate and thus is the most susceptible to noise. Therefore, using a D-term filter allows to decrease the driver-level filtering, which leads to reduced control latency and permits to increase the P gains. A value of 0 disables the filter. + 0 + 1000 + Hz + 0 + 10 + modules/mc_att_control - + Pitch rate D gain Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. 0.0 - examples/mc_att_control_multiplatform - - - Yaw P gain - Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. - 0.0 - 1/s - examples/mc_att_control_multiplatform - - - Yaw rate P gain - Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw rate I gain - Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - examples/mc_att_control_multiplatform - - - Yaw rate D gain - Yaw rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. - 0.0 - examples/mc_att_control_multiplatform + 4 + 0.0005 + modules/mc_att_control - - Yaw feed forward - Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Pitch rate feedforward + Improves tracking performance. 0.0 - 1.0 - examples/mc_att_control_multiplatform + 4 + modules/mc_att_control - - Max yaw rate - Limit for yaw rate, has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. + + Pitch rate I gain + Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform + 3 + 0.01 + modules/mc_att_control - - Max acro roll rate + + Max pitch rate + Limit for pitch rate in manual and auto modes (except acro). Has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. This is not only limited by the vehicle's properties, but also by the maximum measurement rate of the gyro. 0.0 - 360.0 + 1800.0 deg/s - examples/mc_att_control_multiplatform + 1 + 5 + modules/mc_att_control - - Max acro pitch rate + + Pitch rate P gain + Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. 0.0 - 360.0 - deg/s - examples/mc_att_control_multiplatform + 0.6 + 3 + 0.01 + modules/mc_att_control - - Max acro yaw rate + + Pitch P gain + Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 - deg/s - examples/mc_att_control_multiplatform - - - Roll time constant - Reduce if the system is too twitchy, increase if the response is too slow and sluggish. - 0.15 - 0.25 - s + 12 + 1/s 2 - 0.01 + 0.1 modules/mc_att_control @@ -4167,38 +4261,20 @@ Does not affect MAVLINK_ROI input 0.01 modules/mc_att_control - - Roll P gain - Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. + + Pitch rate integrator limit + Pitch rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large pitch moment trim changes. 0.0 - 8 - 1/s 2 - 0.1 + 0.01 modules/mc_att_control - - Roll rate P gain - Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. + + Threshold for Rattitude mode + Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints 0.0 - 0.5 - 3 - 0.01 - modules/mc_att_control - - - Roll rate I gain - Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. - 0.0 - 3 - 0.01 - modules/mc_att_control - - - Roll rate integrator limit - Roll rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large roll moment trim changes. - 0.0 - 2 + 1.0 + 2 0.01 modules/mc_att_control @@ -4218,89 +4294,113 @@ Does not affect MAVLINK_ROI input 4 modules/mc_att_control - - Pitch P gain - Pitch proportional gain, i.e. desired angular speed in rad/s for error 1 rad. + + Roll rate I gain + Roll rate integral gain. Can be set to compensate static thrust difference or gravity center offset. 0.0 - 10 - 1/s - 2 - 0.1 + 3 + 0.01 modules/mc_att_control - - Pitch rate P gain - Pitch rate proportional gain, i.e. control output for angular speed error 1 rad/s. + + Max roll rate + Limit for roll rate in manual and auto modes (except acro). Has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. This is not only limited by the vehicle's properties, but also by the maximum measurement rate of the gyro. 0.0 - 0.6 + 1800.0 + deg/s + 1 + 5 + modules/mc_att_control + + + Roll rate P gain + Roll rate proportional gain, i.e. control output for angular speed error 1 rad/s. + 0.0 + 0.5 3 0.01 modules/mc_att_control - - Pitch rate I gain - Pitch rate integral gain. Can be set to compensate static thrust difference or gravity center offset. + + Roll P gain + Roll proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 - 3 + 12 + 1/s + 2 + 0.1 + modules/mc_att_control + + + Roll time constant + Reduce if the system is too twitchy, increase if the response is too slow and sluggish. + 0.15 + 0.25 + s + 2 0.01 modules/mc_att_control - - Pitch rate integrator limit - Pitch rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large pitch moment trim changes. + + Roll rate integrator limit + Roll rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large roll moment trim changes. 0.0 2 0.01 modules/mc_att_control - - Pitch rate D gain - Pitch rate differential gain. Small values help reduce fast oscillations. If value is too big oscillations will appear again. + + TPA D Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch D gain 0.0 - 4 - 0.0005 + 1.0 + 2 + 0.1 modules/mc_att_control - - Pitch rate feedforward - Improves tracking performance. + + TPA I Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch I gain 0.0 - 4 + 1.0 + 2 + 0.1 modules/mc_att_control - - Yaw P gain - Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. + + TPA P Breakpoint + Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch P gain 0.0 - 5 - 1/s + 1.0 2 0.1 modules/mc_att_control - - Yaw rate P gain - Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. + + TPA Rate D + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch D gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 - 0.6 + 1.0 2 - 0.01 + 0.05 modules/mc_att_control - - Yaw rate I gain - Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. + + TPA Rate I + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch I gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 + 1.0 2 - 0.01 + 0.05 modules/mc_att_control - - Yaw rate integrator limit - Yaw rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large yaw moment trim changes. + + TPA Rate P + Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch P gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) 0.0 + 1.0 2 - 0.01 + 0.05 modules/mc_att_control @@ -4319,28 +4419,16 @@ Does not affect MAVLINK_ROI input 0.01 modules/mc_att_control - - Yaw feed forward - Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Yaw rate I gain + Yaw rate integral gain. Can be set to compensate static thrust difference or gravity center offset. 0.0 - 1.0 2 0.01 modules/mc_att_control - - Max roll rate - Limit for roll rate in manual and auto modes (except acro). Has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. This is not only limited by the vehicle's properties, but also by the maximum measurement rate of the gyro. - 0.0 - 1800.0 - deg/s - 1 - 5 - modules/mc_att_control - - - Max pitch rate - Limit for pitch rate in manual and auto modes (except acro). Has effect for large rotations in autonomous mode, to avoid large control output and mixer saturation. This is not only limited by the vehicle's properties, but also by the maximum measurement rate of the gyro. + + Max yaw rate 0.0 1800.0 deg/s @@ -4348,13 +4436,13 @@ Does not affect MAVLINK_ROI input 5 modules/mc_att_control - - Max yaw rate + + Yaw rate P gain + Yaw rate proportional gain, i.e. control output for angular speed error 1 rad/s. 0.0 - 1800.0 - deg/s - 1 - 5 + 0.6 + 2 + 0.01 modules/mc_att_control @@ -4367,239 +4455,217 @@ Does not affect MAVLINK_ROI input 5 modules/mc_att_control - - Max acro roll rate -default: 2 turns per second - 0.0 - 1000.0 - deg/s - 1 - 5 - modules/mc_att_control - - - Max acro pitch rate -default: 2 turns per second - 0.0 - 1000.0 - deg/s - 1 - 5 - modules/mc_att_control - - - Max acro yaw rate -default 1.5 turns per second - 0.0 - 1000.0 - deg/s - 1 - 5 - modules/mc_att_control - - - Acro Expo factor -applied to input of all axis: roll, pitch, yaw - 0 Purely linear input curve 1 Purely cubic input curve - 0 - 1 - 2 - modules/mc_att_control - - - Acro SuperExpo factor -applied to input of all axis: roll, pitch, yaw - 0 Pure Expo function 0.7 resonable shape enhancement for intuitive stick feel 0.95 very strong bent input curve only near maxima have effect - 0 - 0.95 - 2 - modules/mc_att_control - - - Threshold for Rattitude mode - Manual input needed in order to override attitude control rate setpoints and instead pass manual stick inputs as rate setpoints + + Yaw feed forward + Feed forward weight for manual yaw control. 0 will give slow responce and no overshot, 1 - fast responce and big overshot. 0.0 1.0 2 0.01 modules/mc_att_control - - Battery power level scaler - This compensates for voltage drop of the battery over time by attempting to normalize performance across the operating range of the battery. The copter should constantly behave as if it was fully charged with reduced max acceleration at lower battery percentages. i.e. if hover is at 0.5 throttle at 100% battery, it will still be 0.5 at 60% battery. - - modules/mc_att_control - - - TPA P Breakpoint - Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch P gain + + Yaw P gain + Yaw proportional gain, i.e. desired angular speed in rad/s for error 1 rad. 0.0 - 1.0 + 5 + 1/s 2 0.1 modules/mc_att_control - - TPA I Breakpoint - Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch I gain + + Yaw rate integrator limit + Yaw rate integrator limit. Can be set to increase the amount of integrator available to counteract disturbances or reduced to improve settling time after large yaw moment trim changes. 0.0 - 1.0 2 - 0.1 + 0.01 modules/mc_att_control - - TPA D Breakpoint - Throttle PID Attenuation (TPA) Magnitude of throttle setpoint at which to begin attenuating roll/pitch D gain - 0.0 - 1.0 + + + + Maximum vertical acceleration in velocity controlled modes down + 2.0 + 15.0 + m/s/s 2 - 0.1 - modules/mc_att_control + 1 + modules/mc_pos_control - - TPA Rate P - Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch P gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) - 0.0 - 1.0 + + Acceleration for auto and for manual + 2.0 + 15.0 + m/s/s 2 - 0.05 - modules/mc_att_control + 1 + modules/mc_pos_control - - TPA Rate I - Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch I gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) - 0.0 - 1.0 - 2 - 0.05 - modules/mc_att_control + + Horizontal acceleration in manual modes when optical flow ground speed limit is removed. +If full stick is being applied and the EKF starts using GPS whilst using optical flow, +the vehicle will accelerate at this rate until the normal position control speed is achieved + 0.2 + 2.0 + m/s/s + 1 + 0.1 + modules/mc_pos_control - - TPA Rate D - Throttle PID Attenuation (TPA) Rate at which to attenuate roll/pitch D gain Attenuation factor is 1.0 when throttle magnitude is below the setpoint Above the setpoint, the attenuation factor is (1 - rate * (throttle - breakpoint) / (1.0 - breakpoint)) - 0.0 - 1.0 + + Maximum horizontal acceleration for auto mode and maximum deceleration for manual mode + 2.0 + 15.0 + m/s/s 2 - 0.05 - modules/mc_att_control - - - - - Minimum thrust - Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform + 1 + modules/mc_pos_control - - Maximum thrust - Limit max allowed thrust. - 0.0 - 1.0 - examples/mc_pos_control_multiplatform + + Maximum vertical acceleration in velocity controlled modes upward + 2.0 + 15.0 + m/s/s + 2 + 1 + modules/mc_pos_control - - Proportional gain for vertical position error - 0.0 - examples/mc_pos_control_multiplatform + + Altitude control mode, note mode 1 only tested with LPE + 0 + 1 + modules/mc_pos_control + + Altitude following + Terrain following + - - Proportional gain for vertical velocity error - 0.0 - examples/mc_pos_control_multiplatform + + Cruise speed when angle prev-current/current-next setpoint +is 90 degrees. It should be lower than MPC_XY_CRUISE + Applies only in AUTO modes (includes also RTL / hold / etc.) + 1.0 + 20.0 + m/s + 2 + 1 + modules/mc_pos_control - - Integral gain for vertical velocity error - Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. - 0.0 - examples/mc_pos_control_multiplatform + + Slow horizontal manual deceleration for manual mode + 0.5 + 10.0 + m/s/s + 2 + 1 + modules/mc_pos_control - - Differential gain for vertical velocity error + + Deadzone of sticks where position hold is enabled 0.0 - examples/mc_pos_control_multiplatform + 1.0 + 2 + modules/mc_pos_control - - Maximum vertical velocity - Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL). + + Maximum horizontal velocity for which position hold is enabled (use 0 to disable check) 0.0 + 3.0 m/s - examples/mc_pos_control_multiplatform + 2 + modules/mc_pos_control - - Vertical velocity feed forward - Feed forward weight for altitude control in stabilized modes (ALTCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Maximum vertical velocity for which position hold is enabled (use 0 to disable check) 0.0 - 1.0 - examples/mc_pos_control_multiplatform + 3.0 + m/s + 2 + modules/mc_pos_control - - Proportional gain for horizontal position error + + Maximum jerk in manual controlled mode for BRAKING to zero. +If this value is below MPC_JERK_MIN, the acceleration limit in xy and z +is MPC_ACC_HOR_MAX and MPC_ACC_UP_MAX respectively instantaneously when the +user demands brake (=zero stick input). +Otherwise the acceleration limit increases from current acceleration limit +towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 0.0 - examples/mc_pos_control_multiplatform + 15.0 + m/s/s/s + 2 + 1 + modules/mc_pos_control - - Proportional gain for horizontal velocity error - 0.0 - examples/mc_pos_control_multiplatform + + Minimum jerk in manual controlled mode for BRAKING to zero + 0.5 + 10.0 + m/s/s/s + 2 + 1 + modules/mc_pos_control - - Integral gain for horizontal velocity error - Non-zero value allows to resist wind. - 0.0 - examples/mc_pos_control_multiplatform + + Altitude for 1. step of slow landing (descend) + Below this altitude descending velocity gets limited to a value between "MPC_Z_VEL_MAX" and "MPC_LAND_SPEED" to enable a smooth descent experience Value needs to be higher than "MPC_LAND_ALT2" + 0 + 122 + m + 1 + modules/mc_pos_control - - Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again - 0.0 - examples/mc_pos_control_multiplatform + + Altitude for 2. step of slow landing (landing) + Below this altitude descending velocity gets limited to "MPC_LAND_SPEED" Value needs to be lower than "MPC_LAND_ALT1" + 0 + 122 + m + 1 + modules/mc_pos_control - - Maximum horizontal velocity - Maximum horizontal velocity in AUTO mode and endpoint for position stabilized mode (POSCTRL). - 0.0 + + Landing descend rate + 0.6 m/s - examples/mc_pos_control_multiplatform + 1 + modules/mc_pos_control - - Horizontal velocity feed forward - Feed forward weight for position control in position control mode (POSCTRL). 0 will give slow responce and no overshot, 1 - fast responce and big overshot. + + Maximum manual thrust + Limit max allowed thrust. Setting a value of one can put the system into actuator saturation as no spread between the motors is possible any more. A value of 0.8 - 0.9 is recommended. 0.0 1.0 - examples/mc_pos_control_multiplatform + norm + 2 + 0.01 + modules/mc_pos_control - - Maximum tilt angle in air - Limits maximum tilt in AUTO and POSCTRL modes during flight. + + Minimum manual thrust + Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. 0.0 - 90.0 - deg - examples/mc_pos_control_multiplatform + 1.0 + norm + 2 + 0.01 + modules/mc_pos_control - - Maximum tilt during landing - Limits maximum tilt angle on landing. + + Maximal tilt angle in manual or altitude mode 0.0 90.0 deg - examples/mc_pos_control_multiplatform + 1 + modules/mc_pos_control - - Landing descend rate + + Max manual yaw rate 0.0 - m/s - examples/mc_pos_control_multiplatform - - - Minimum thrust in auto thrust control - It's recommended to set it > 0 to avoid free fall with zero thrust. - 0.05 - 1.0 - norm - 2 - 0.01 + 400 + deg/s + 1 modules/mc_pos_control @@ -4622,70 +4688,84 @@ applied to input of all axis: roll, pitch, yaw 0.01 modules/mc_pos_control - - Minimum manual thrust - Minimum vertical thrust. It's recommended to set it > 0 to avoid free fall with zero thrust. - 0.0 + + Minimum thrust in auto thrust control + It's recommended to set it > 0 to avoid free fall with zero thrust. + 0.05 1.0 norm 2 0.01 modules/mc_pos_control - - Maximum manual thrust - Limit max allowed thrust. Setting a value of one can put the system into actuator saturation as no spread between the motors is possible any more. A value of 0.8 - 0.9 is recommended. + + Maximum tilt angle in air + Limits maximum tilt in AUTO and POSCTRL modes during flight. 0.0 - 1.0 - norm - 2 - 0.01 + 90.0 + deg + 1 modules/mc_pos_control - - Proportional gain for vertical position error + + Maximum tilt during landing + Limits maximum tilt angle on landing. 0.0 - 1.5 - 2 + 90.0 + deg + 1 modules/mc_pos_control - - Proportional gain for vertical velocity error + + Position control smooth takeoff ramp time constant + Increasing this value will make automatic and manual takeoff slower. If it's too slow the drone might scratch the ground and tip over. 0.1 - 0.4 - 2 + 1 modules/mc_pos_control - - Integral gain for vertical velocity error - Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. - 0.01 - 0.1 - 3 + + Takeoff climb rate + 1 + 5 + m/s + 2 modules/mc_pos_control - - Differential gain for vertical velocity error + + Low pass filter cut freq. for numerical velocity derivative 0.0 - 0.1 - 3 + 10 + Hz + 2 modules/mc_pos_control - - Maximum vertical ascent velocity - Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL, POSCTRL). - 0.5 - 8.0 + + Maximum horizontal velocity setpoint for manual controlled mode +If velocity setpoint larger than MPC_XY_VEL_MAX is set, then +the setpoint will be capped to MPC_XY_VEL_MAX + 3.0 + 20.0 m/s - 1 + 2 + 1 modules/mc_pos_control - - Maximum vertical descent velocity - Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL, POSCTRL). - 0.5 - 4.0 + + Maximum horizontal velocity in mission + Normal horizontal velocity in AUTO modes (includes also RTL / hold / etc.) and endpoint for position stabilized mode (POSCTRL). + 3.0 + 20.0 m/s + 2 + 1 + modules/mc_pos_control + + + Manual control stick exponential curve sensitivity attenuation with small velocity setpoints + The higher the value the less sensitivity the stick has around zero while still reaching the maximum value with full stick deflection. 0 Purely linear input curve (default) 1 Purely cubic input curve + 0 + 1 + 2 modules/mc_pos_control @@ -4695,11 +4775,11 @@ applied to input of all axis: roll, pitch, yaw 2 modules/mc_pos_control - - Proportional gain for horizontal velocity error - 0.06 - 0.15 - 2 + + Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again + 0.005 + 0.1 + 3 modules/mc_pos_control @@ -4710,295 +4790,201 @@ applied to input of all axis: roll, pitch, yaw 3 modules/mc_pos_control - - Differential gain for horizontal velocity error. Small values help reduce fast oscillations. If value is too big oscillations will appear again - 0.005 - 0.1 - 3 - modules/mc_pos_control - - - Maximum horizontal velocity in mission - Normal horizontal velocity in AUTO modes (includes also RTL / hold / etc.) and endpoint for position stabilized mode (POSCTRL). - 3.0 + + Maximum horizontal velocity + Maximum horizontal velocity in AUTO mode. If higher speeds are commanded in a mission they will be capped to this velocity. + 0.0 20.0 m/s 2 1 modules/mc_pos_control - - Cruise speed when angle prev-current/current-next setpoint -is 90 degrees. It should be lower than MPC_XY_CRUISE - Applies only in AUTO modes (includes also RTL / hold / etc.) - 1.0 - m/s + + Proportional gain for horizontal velocity error + 0.06 + 0.15 2 - 1 modules/mc_pos_control - - Maximum horizontal velocity setpoint for manual controlled mode -If velocity setpoint larger than MPC_XY_VEL_MAX is set, then -the setpoint will be capped to MPC_XY_VEL_MAX - 3.0 - 20.0 - m/s + + Manual control stick vertical exponential curve + The higher the value the less sensitivity the stick has around zero while still reaching the maximum value with full stick deflection. 0 Purely linear input curve (default) 1 Purely cubic input curve + 0 + 1 2 - 1 modules/mc_pos_control - - Maximum horizontal velocity - Maximum horizontal velocity in AUTO mode. If higher speeds are commanded in a mission they will be capped to this velocity. + + Proportional gain for vertical position error 0.0 - 20.0 - m/s + 1.5 2 - 1 modules/mc_pos_control - - Maximum tilt angle in air - Limits maximum tilt in AUTO and POSCTRL modes during flight. + + Differential gain for vertical velocity error 0.0 - 90.0 - deg - 1 + 0.1 + 3 modules/mc_pos_control - - Maximum tilt during landing - Limits maximum tilt angle on landing. - 0.0 - 90.0 - deg - 1 + + Integral gain for vertical velocity error + Non zero value allows hovering thrust estimation on stabilized or autonomous takeoff. + 0.01 + 0.1 + 3 modules/mc_pos_control - - Landing descend rate - 0.6 + + Maximum vertical descent velocity + Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL, POSCTRL). + 0.5 + 4.0 m/s - 1 modules/mc_pos_control - - Takeoff climb rate - 1 - 5 + + Maximum vertical ascent velocity + Maximum vertical velocity in AUTO mode and endpoint for stabilized modes (ALTCTRL, POSCTRL). + 0.5 + 8.0 m/s - 2 - modules/mc_pos_control - - - Maximal tilt angle in manual or altitude mode - 0.0 - 90.0 - deg 1 modules/mc_pos_control - - Max manual yaw rate - 0.0 - 400 - deg/s - 1 - modules/mc_pos_control - - - Deadzone of sticks where position hold is enabled - 0.0 - 1.0 + + Proportional gain for vertical velocity error + 0.1 + 0.4 2 modules/mc_pos_control - - Maximum horizontal velocity for which position hold is enabled (use 0 to disable check) + + + + Minimum motor rise time (slew rate limit) + Minimum time allowed for the motor input signal to pass through a range of 1000 PWM units. A value x means that the motor signal can only go from 1000 to 2000 PWM in maximum x seconds. Zero means that slew rate limiting is disabled. 0.0 - 3.0 - m/s - 2 - modules/mc_pos_control + s/(1000*PWM) + drivers/px4fmu - - Maximum vertical velocity for which position hold is enabled (use 0 to disable check) - 0.0 - 3.0 - m/s - 2 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 1 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Low pass filter cut freq. for numerical velocity derivative - 0.0 - 10 - Hz - 2 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 2 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Maximum horizontal acceleration for auto mode and maximum deceleration for manual mode - 2.0 - 15.0 - m/s/s - 2 - 1 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 3 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Acceleration for auto and for manual - 2.0 - 15.0 - m/s/s - 2 - 1 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 4 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Slow horizontal manual deceleration for manual mode - 0.5 - 10.0 - m/s/s - 2 - 1 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 5 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Maximum vertical acceleration in velocity controlled modes upward - 2.0 - 15.0 - m/s/s - 2 - 1 - modules/mc_pos_control - - - Maximum vertical acceleration in velocity controlled modes down - 2.0 - 15.0 - m/s/s - 2 - 1 - modules/mc_pos_control - - - Maximum jerk in manual controlled mode for BRAKING to zero. -If this value is below MPC_JERK_MIN, the acceleration limit in xy and z -is MPC_ACC_HOR_MAX and MPC_ACC_UP_MAX respectively instantaneously when the -user demands brake (=zero stick input). -Otherwise the acceleration limit increases from current acceleration limit -towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit - 0.0 - 15.0 - m/s/s/s - 2 - 1 - modules/mc_pos_control - - - Minimum jerk in manual controlled mode for BRAKING to zero - 0.5 - 10.0 - m/s/s/s - 2 - 1 - modules/mc_pos_control - - - Altitude control mode, note mode 1 only tested with LPE - 0 - 1 - modules/mc_pos_control - - Terrain following - Altitude following - - - - Manual control stick exponential curve sensitivity attenuation with small velocity setpoints - The higher the value the less sensitivity the stick has around zero while still reaching the maximum value with full stick deflection. 0 Purely linear input curve (default) 1 Purely cubic input curve - 0 - 1 - 2 - modules/mc_pos_control - - - Manual control stick vertical exponential curve - The higher the value the less sensitivity the stick has around zero while still reaching the maximum value with full stick deflection. 0 Purely linear input curve (default) 1 Purely cubic input curve - 0 - 1 - 2 - modules/mc_pos_control + + Set the disarmed PWM for the AUX 6 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used + -1 + 2200 + us + true + modules/sensors - - Altitude for 1. step of slow landing (descend) - Below this altitude descending velocity gets limited to a value between "MPC_Z_VEL_MAX" and "MPC_LAND_SPEED" to enable a smooth descent experience Value needs to be higher than "MPC_LAND_ALT2" + + Set the disarmed PWM for auxiliary outputs + This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. 0 - 122 - m - 1 - modules/mc_pos_control + 2200 + us + true + modules/sensors - - Altitude for 2. step of slow landing (landing) - Below this altitude descending velocity gets limited to "MPC_LAND_SPEED" Value needs to be lower than "MPC_LAND_ALT1" - 0 - 122 - m - 1 - modules/mc_pos_control + + Set the maximum PWM for the auxiliary outputs + Set to 2000 for default or 2100 to increase servo travel + 1600 + 2200 + us + true + modules/sensors - - Position control smooth takeoff ramp time constant - Increasing this value will make automatic and manual takeoff slower. If it's too slow the drone might scratch the ground and tip over. - 0.1 - 1 - modules/mc_pos_control + + Set the minimum PWM for the auxiliary outputs + Set to 1000 for default or 900 to increase servo travel + 800 + 1400 + us + true + modules/sensors - - Invert direction of aux output channel 1 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu Invert direction of aux output channel 2 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu Invert direction of aux output channel 3 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu Invert direction of aux output channel 4 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu Invert direction of aux output channel 5 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu Invert direction of aux output channel 6 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4fmu @@ -5049,74 +5035,133 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 2 drivers/px4fmu - - Thrust to PWM model parameter - Parameter used to model the relationship between static thrust and motor input PWM. Model is: thrust = (1-factor)*PWM + factor * PWM^2 - 0.0 - 1.0 - drivers/px4fmu + + Set the disarmed PWM for the main outputs + This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. + 0 + 2200 + us + true + modules/sensors - - Minimum motor rise time (slew rate limit) - Minimum time allowed for the motor input signal to pass through a range of 1000 PWM units. A value x means that the motor signal can only go from 1000 to 2000 PWM in maximum x seconds. Zero means that slew rate limiting is disabled. - 0.0 - s/(1000*PWM) - drivers/px4fmu + + Set the disarmed PWM for the main 1 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 2 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 3 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 4 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 5 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 6 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 7 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors + + + Set the disarmed PWM for the main 8 output + This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used + -1 + 2200 + us + true + modules/sensors Invert direction of main output channel 1 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 2 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 3 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 4 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 5 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 6 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 7 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io Invert direction of main output channel 8 - Set to 1 to invert the channel, 0 for default direction. + Enable to invert the channel. - true drivers/px4io @@ -5183,18 +5228,12 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 2 drivers/px4io - - S.BUS out - Set to 1 to enable S.BUS version 1 output instead of RSSI. - - drivers/px4io - - - Set the PWM output frequency for the main outputs - Set to 400 for industry default or 1000 for high frequency ESCs. Set to 0 for Oneshot125. - -1 - 2000 - Hz + + Set the maximum PWM for the main outputs + Set to 2000 for industry default or 2100 to increase servo travel. + 1600 + 2200 + us true modules/sensors @@ -5207,176 +5246,27 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit true modules/sensors - - Set the maximum PWM for the main outputs - Set to 2000 for industry default or 2100 to increase servo travel. - 1600 - 2200 - us + + Set the PWM output frequency for the main outputs + Set to 400 for industry default or 1000 for high frequency ESCs. Set to 0 for Oneshot125. + -1 + 2000 + Hz true modules/sensors - - Set the disarmed PWM for the main outputs - This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. - 0 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 1 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 2 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 3 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 4 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 5 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 6 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 7 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the main 8 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 1 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 2 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 3 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 4 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 5 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the disarmed PWM for the AUX 6 output - This is the PWM pulse the autopilot is outputting if not armed. When set to -1 the value for PWM_AUX_DISARMED will be used - -1 - 2200 - us - true - modules/sensors - - - Set the minimum PWM for the auxiliary outputs - Set to 1000 for default or 900 to increase servo travel - 800 - 1400 - us - true - modules/sensors - - - Set the maximum PWM for the auxiliary outputs - Set to 2000 for default or 2100 to increase servo travel - 1600 - 2200 - us - true - modules/sensors + + S.BUS out + Set to 1 to enable S.BUS version 1 output instead of RSSI. + + drivers/px4io - - Set the disarmed PWM for auxiliary outputs - This is the PWM pulse the autopilot is outputting if not armed. The main use of this parameter is to silence ESCs when they are disarmed. - 0 - 2200 - us - true - modules/sensors + + Thrust to PWM model parameter + Parameter used to model the relationship between static thrust and motor input PWM. Model is: thrust = (1-factor)*PWM + factor * PWM^2 + 0.0 + 1.0 + drivers/px4fmu @@ -5387,22 +5277,6 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 0.1 examples/bottle_drop - - Plane turn radius - The planes known minimal turn radius - use a higher value to make the plane maneuver more distant from the actual drop position. This is to ensure the wings are level during the drop. - 30.0 - 500.0 - m - examples/bottle_drop - - - Drop precision - If the system is closer than this distance on passing over the drop position, it will release the payload. This is a safeguard to prevent a drop out of the required accuracy. - 1.0 - 80.0 - m - examples/bottle_drop - Payload drag coefficient of the dropped object The drag coefficient (cd) is the typical drag constant for air. It is in general object specific, but the closest primitive shape to the actual object should give good results: http://en.wikipedia.org/wiki/Drag_coefficient @@ -5426,200 +5300,147 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit m^2 examples/bottle_drop - - - - Velocity estimate delay - The delay in milliseconds of the velocity estimate from GPS. - 0 - 1000 - ms - examples/ekf_att_pos_estimator - - - Position estimate delay - The delay in milliseconds of the position estimate from GPS. - 0 - 1000 - ms - examples/ekf_att_pos_estimator - - - Height estimate delay - The delay in milliseconds of the height estimate from the barometer. - 0 - 1000 - ms - examples/ekf_att_pos_estimator + + Drop precision + If the system is closer than this distance on passing over the drop position, it will release the payload. This is a safeguard to prevent a drop out of the required accuracy. + 1.0 + 80.0 + m + examples/bottle_drop - - Mag estimate delay - The delay in milliseconds of the magnetic field estimate from the magnetometer. - 0 - 1000 - ms - examples/ekf_att_pos_estimator + + Plane turn radius + The planes known minimal turn radius - use a higher value to make the plane maneuver more distant from the actual drop position. This is to ensure the wings are level during the drop. + 30.0 + 500.0 + m + examples/bottle_drop - - True airspeeed estimate delay - The delay in milliseconds of the airspeed estimate. + + + + Disable vision input + Set to the appropriate key (328754) to disable vision input. 0 - 1000 - ms - examples/ekf_att_pos_estimator + 328754 + true + modules/position_estimator_inav - - GPS vs. barometric altitude update weight - RE-CHECK this. + + GPS delay + GPS delay compensation 0.0 1.0 - examples/ekf_att_pos_estimator - - - Airspeed measurement noise - Increasing this value will make the filter trust this sensor less and trust other sensors more. - 0.5 - 5.0 - examples/ekf_att_pos_estimator - - - Velocity measurement noise in north-east (horizontal) direction - Generic default: 0.3, multicopters: 0.5, ground vehicles: 0.5 - 0.05 - 5.0 - examples/ekf_att_pos_estimator - - - Velocity noise in down (vertical) direction - Generic default: 0.3, multicopters: 0.4, ground vehicles: 0.7 - 0.2 - 3.0 - examples/ekf_att_pos_estimator - - - Position noise in north-east (horizontal) direction - Generic defaults: 0.5, multicopters: 0.5, ground vehicles: 0.5 - 0.1 - 10.0 - examples/ekf_att_pos_estimator + s + modules/position_estimator_inav - - Position noise in down (vertical) direction - Generic defaults: 1.25, multicopters: 1.0, ground vehicles: 1.0 - 0.5 - 3.0 - examples/ekf_att_pos_estimator + + Mo-cap + Set to 0 if using fake GPS + modules/position_estimator_inav + + Mo-cap enabled + Mo-cap disabled + - - Magnetometer measurement noise - Generic defaults: 0.05, multicopters: 0.05, ground vehicles: 0.05 - 0.01 + + Flow module offset (center of rotation) in X direction + Yaw X flow compensation + -1.0 1.0 - examples/ekf_att_pos_estimator - - - Gyro process noise - Generic defaults: 0.015, multicopters: 0.015, ground vehicles: 0.015. This noise controls how much the filter trusts the gyro measurements. Increasing it makes the filter trust the gyro less and other sensors more. - 0.001 - 0.05 - examples/ekf_att_pos_estimator + m + modules/position_estimator_inav - - Accelerometer process noise - Generic defaults: 0.25, multicopters: 0.25, ground vehicles: 0.25. Increasing this value makes the filter trust the accelerometer less and other sensors more. - 0.05 + + Flow module offset (center of rotation) in Y direction + Yaw Y flow compensation + -1.0 1.0 - examples/ekf_att_pos_estimator - - - Gyro bias estimate process noise - Generic defaults: 1e-07f, multicopters: 1e-07f, ground vehicles: 1e-07f. Increasing this value will make the gyro bias converge faster but noisier. - 0.00000005 - 0.00001 - examples/ekf_att_pos_estimator - - - Accelerometer bias estimate process noise - Generic defaults: 0.00001f, multicopters: 0.00001f, ground vehicles: 0.00001f. Increasing this value makes the bias estimation faster and noisier. - 0.00001 - 0.001 - examples/ekf_att_pos_estimator - - - Magnetometer earth frame offsets process noise - Generic defaults: 0.0001, multicopters: 0.0001, ground vehicles: 0.0001. Increasing this value makes the magnetometer earth bias estimate converge faster but also noisier. - 0.0001 - 0.01 - examples/ekf_att_pos_estimator - - - Magnetometer body frame offsets process noise - Generic defaults: 0.0003, multicopters: 0.0003, ground vehicles: 0.0003. Increasing this value makes the magnetometer body bias estimate converge faster but also noisier. - 0.0001 - 0.01 - examples/ekf_att_pos_estimator - - - Magnetometer X bias - The magnetometer bias. This bias is learnt by the filter over time and persists between boots. - -0.6 - 0.6 - examples/ekf_att_pos_estimator + m + modules/position_estimator_inav - - Magnetometer Y bias - The magnetometer bias. This bias is learnt by the filter over time and persists between boots. - -0.6 - 0.6 - examples/ekf_att_pos_estimator + + Optical flow scale factor + Factor to scale optical flow + 0.0 + 10.0 + modules/position_estimator_inav - - Magnetometer Z bias - The magnetometer bias. This bias is learnt by the filter over time and persists between boots. - -0.6 - 0.6 - examples/ekf_att_pos_estimator + + Minimal acceptable optical flow quality + 0 - lowest quality, 1 - best quality. + 0.0 + 1.0 + modules/position_estimator_inav - - Threshold for filter initialization - If the standard deviation of the GPS position estimate is below this threshold in meters, the filter will initialize. - 0.3 + + Land detector altitude dispersion threshold + Dispersion threshold for triggering land detector. + 0.0 10.0 - examples/ekf_att_pos_estimator + m + modules/position_estimator_inav - - - - Z axis weight for barometer - Weight (cutoff frequency) for barometer altitude measurements. + + Land detector time + Vehicle assumed landed if no altitude changes happened during this time on low throttle. 0.0 10.0 + s modules/position_estimator_inav - - Z axis weight for GPS - Weight (cutoff frequency) for GPS altitude measurements. GPS altitude data is very noisy and should be used only as slow correction for baro offset. + + Land detector throttle threshold + Value should be lower than minimal hovering thrust. Half of it is good choice. 0.0 - 10.0 + 1.0 modules/position_estimator_inav - - Z velocity weight for GPS - Weight (cutoff frequency) for GPS altitude velocity measurements. + + Sonar maximal error for new surface + If sonar measurement error is larger than this value it skiped (spike) or accepted as new surface level (if offset is stable). 0.0 - 10.0 + 1.0 + m modules/position_estimator_inav - - Z axis weight for vision - Weight (cutoff frequency) for vision altitude measurements. vision altitude data is very noisy and should be used only as slow correction for baro offset. + + LIDAR for altitude estimation + + modules/position_estimator_inav + + + LIDAR calibration offset + LIDAR calibration offset. Value will be added to the measured distance + -20 + 20 + m + modules/position_estimator_inav + + + Accelerometer bias estimation weight + Weight (cutoff frequency) for accelerometer bias estimation. 0 to disable. + 0.0 + 0.1 + modules/position_estimator_inav + + + XY axis weight factor for GPS when optical flow available + When optical flow data available, multiply GPS weights (for position and velocity) by this factor. + 0.0 + 1.0 + modules/position_estimator_inav + + + Weight for mocap system + Weight (cutoff frequency) for mocap position measurements. 0.0 10.0 modules/position_estimator_inav - - Z axis weight for lidar - Weight (cutoff frequency) for lidar measurements. + + XY axis weight for optical flow + Weight (cutoff frequency) for optical flow (velocity) measurements. 0.0 10.0 modules/position_estimator_inav @@ -5638,6 +5459,13 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 10.0 modules/position_estimator_inav + + XY axis weight for resetting velocity + When velocity sources lost slowly decrease estimated horizontal velocity with this weight. + 0.0 + 10.0 + modules/position_estimator_inav + XY axis weight for vision position Weight (cutoff frequency) for vision position measurements. @@ -5652,949 +5480,1034 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 10.0 modules/position_estimator_inav - - Weight for mocap system - Weight (cutoff frequency) for mocap position measurements. + + Z axis weight for barometer + Weight (cutoff frequency) for barometer altitude measurements. 0.0 10.0 modules/position_estimator_inav - - XY axis weight for optical flow - Weight (cutoff frequency) for optical flow (velocity) measurements. + + Z axis weight for GPS + Weight (cutoff frequency) for GPS altitude measurements. GPS altitude data is very noisy and should be used only as slow correction for baro offset. 0.0 10.0 modules/position_estimator_inav - - XY axis weight for resetting velocity - When velocity sources lost slowly decrease estimated horizontal velocity with this weight. + + Z velocity weight for GPS + Weight (cutoff frequency) for GPS altitude velocity measurements. 0.0 10.0 modules/position_estimator_inav - - XY axis weight factor for GPS when optical flow available - When optical flow data available, multiply GPS weights (for position and velocity) by this factor. - 0.0 - 1.0 - modules/position_estimator_inav - - - Accelerometer bias estimation weight - Weight (cutoff frequency) for accelerometer bias estimation. 0 to disable. + + Z axis weight for lidar + Weight (cutoff frequency) for lidar measurements. 0.0 - 0.1 + 10.0 modules/position_estimator_inav - - Optical flow scale factor - Factor to scale optical flow + + Z axis weight for vision + Weight (cutoff frequency) for vision altitude measurements. vision altitude data is very noisy and should be used only as slow correction for baro offset. 0.0 10.0 modules/position_estimator_inav - - Minimal acceptable optical flow quality - 0 - lowest quality, 1 - best quality. + + + + Landing Target Timeout + Time after which the landing target is considered lost without any new measurements. 0.0 - 1.0 - modules/position_estimator_inav + 50 + s + 1 + 0.5 + modules/navigator - - Sonar maximal error for new surface - If sonar measurement error is larger than this value it skiped (spike) or accepted as new surface level (if offset is stable). + + Final approach altitude + Allow final approach (without horizontal positioning) if losing landing target closer than this to the ground. 0.0 - 1.0 + 10 m - modules/position_estimator_inav + 2 + 0.1 + modules/navigator - - Land detector time - Vehicle assumed landed if no altitude changes happened during this time on low throttle. + + Horizontal acceptance radius + Start descending if closer above landing target than this. 0.0 - 10.0 - s - modules/position_estimator_inav + 10 + m + 2 + 0.1 + modules/navigator - - Land detector altitude dispersion threshold - Dispersion threshold for triggering land detector. + + Maximum number of search attempts + Maximum number of times to seach for the landing target if it is lost during the precision landing. + 0 + 100 + modules/navigator + + + Search altitude + Altitude above home to which to climb when searching for the landing target. 0.0 - 10.0 + 100 m - modules/position_estimator_inav + 1 + 0.1 + modules/navigator - - Land detector throttle threshold - Value should be lower than minimal hovering thrust. Half of it is good choice. + + Search timeout + Time allowed to search for the landing target before falling back to normal landing. 0.0 - 1.0 - modules/position_estimator_inav + 100 + s + 1 + 0.1 + modules/navigator - - GPS delay - GPS delay compensation + + + + RC receiver type + Acceptable values: - RC_RECEIVER_SPEKTRUM = 1, - RC_RECEIVER_LEMONRX = 2, + platforms/qurt/fc_addon/rc_receiver + + + + + RC channel 10 dead zone + The +- range of this value around the trim value will be considered as zero. 0.0 - 1.0 - s - modules/position_estimator_inav + 100.0 + modules/sensors - - Flow module offset (center of rotation) in X direction - Yaw X flow compensation - -1.0 - 1.0 - m - modules/position_estimator_inav + + RC channel 10 maximum + Maximum value for this channel. + 1500.0 + 2200.0 + us + modules/sensors - - Flow module offset (center of rotation) in Y direction - Yaw Y flow compensation + + RC channel 10 minimum + Minimum value for this channel. + 800.0 + 1500.0 + us + modules/sensors + + + RC channel 10 reverse + Set to -1 to reverse channel. -1.0 1.0 - m - modules/position_estimator_inav - - - Mo-cap - Set to 0 if using fake GPS - modules/position_estimator_inav + modules/sensors - Mo-cap disabled - Mo-cap enabled + Reverse + Normal - - LIDAR for altitude estimation - - modules/position_estimator_inav - - - LIDAR calibration offset - LIDAR calibration offset. Value will be added to the measured distance - -20 - 20 - m - modules/position_estimator_inav - - - Disable vision input - Set to the appropriate key (328754) to disable vision input. - 0 - 328754 - true - modules/position_estimator_inav - - - - - PWM input channel that provides RSSI - 0: do not read RSSI from input channel 1-18: read RSSI from specified input channel Specify the range for RSSI input with RC_RSSI_PWM_MIN and RC_RSSI_PWM_MAX parameters. - 0 - 18 - drivers/px4io - - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 - - - - Max input value for RSSI reading - Only used if RC_RSSI_PWM_CHAN > 0 - 0 - 2000 - drivers/px4io - - - Min input value for RSSI reading - Only used if RC_RSSI_PWM_CHAN > 0 - 0 - 2000 - drivers/px4io - - - RC channel 1 minimum - Minimum value for RC channel 1 - 800.0 - 1500.0 - us - modules/sensors - - - RC channel 1 trim - Mid point value (same as min for throttle) + + RC channel 10 trim + Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 1 maximum - Maximum value for RC channel 1 - 1500.0 - 2200.0 - us - modules/sensors - - - RC channel 1 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 1 dead zone + + RC channel 11 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 + modules/sensors + + + RC channel 11 maximum + Maximum value for this channel. + 1500.0 + 2200.0 us modules/sensors - - RC channel 2 minimum + + RC channel 11 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 2 trim + + RC channel 11 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 11 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 2 maximum + + RC channel 12 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 12 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 2 reverse + + RC channel 12 minimum + Minimum value for this channel. + 800.0 + 1500.0 + us + modules/sensors + + + RC channel 12 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + + + + RC channel 12 trim + Mid point value (has to be set to the same as min for throttle channel). + 800.0 + 2200.0 + us + modules/sensors - - RC channel 2 dead zone + + RC channel 13 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 + modules/sensors + + + RC channel 13 maximum + Maximum value for this channel. + 1500.0 + 2200.0 us modules/sensors - - RC channel 3 minimum + + RC channel 13 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 3 trim + + RC channel 13 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 13 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 3 maximum + + RC channel 14 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 14 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 3 reverse + + RC channel 14 minimum + Minimum value for this channel. + 800.0 + 1500.0 + us + modules/sensors + + + RC channel 14 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + - - RC channel 3 dead zone + + RC channel 14 trim + Mid point value (has to be set to the same as min for throttle channel). + 800.0 + 2200.0 + us + modules/sensors + + + RC channel 15 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 + modules/sensors + + + RC channel 15 maximum + Maximum value for this channel. + 1500.0 + 2200.0 us modules/sensors - - RC channel 4 minimum + + RC channel 15 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 4 trim + + RC channel 15 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 15 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 4 maximum + + RC channel 16 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 16 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 4 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 4 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - us - modules/sensors - - - RC channel 5 minimum + + RC channel 16 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 5 trim + + RC channel 16 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 16 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 5 maximum + + RC channel 17 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 17 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 5 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 5 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 6 minimum + + RC channel 17 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 6 trim + + RC channel 17 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 17 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 6 maximum + + RC channel 18 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 18 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 6 reverse + + RC channel 18 minimum + Minimum value for this channel. + 800.0 + 1500.0 + us + modules/sensors + + + RC channel 18 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + - - RC channel 6 dead zone + + RC channel 18 trim + Mid point value (has to be set to the same as min for throttle channel). + 800.0 + 2200.0 + us + modules/sensors + + + RC channel 1 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 - modules/sensors - - - RC channel 7 minimum - Minimum value for this channel. - 800.0 - 1500.0 us modules/sensors - - RC channel 7 trim - Mid point value (has to be set to the same as min for throttle channel). - 800.0 + + RC channel 1 maximum + Maximum value for RC channel 1 + 1500.0 2200.0 us modules/sensors - - RC channel 7 maximum - Maximum value for this channel. - 1500.0 - 2200.0 + + RC channel 1 minimum + Minimum value for RC channel 1 + 800.0 + 1500.0 us modules/sensors - - RC channel 7 reverse + + RC channel 1 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + - - RC channel 7 dead zone + + RC channel 1 trim + Mid point value (same as min for throttle) + 800.0 + 2200.0 + us + modules/sensors + + + RC channel 2 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 + us modules/sensors - - RC channel 8 minimum + + RC channel 2 maximum + Maximum value for this channel. + 1500.0 + 2200.0 + us + modules/sensors + + + RC channel 2 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 8 trim + + RC channel 2 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 2 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 8 maximum + + RC channel 3 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + us + modules/sensors + + + RC channel 3 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 8 reverse + + RC channel 3 minimum + Minimum value for this channel. + 800.0 + 1500.0 + us + modules/sensors + + + RC channel 3 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + - - RC channel 8 dead zone + + RC channel 3 trim + Mid point value (has to be set to the same as min for throttle channel). + 800.0 + 2200.0 + us + modules/sensors + + + RC channel 4 dead zone The +- range of this value around the trim value will be considered as zero. 0.0 100.0 + us modules/sensors - - RC channel 9 minimum + + RC channel 4 maximum + Maximum value for this channel. + 1500.0 + 2200.0 + us + modules/sensors + + + RC channel 4 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 9 trim + + RC channel 4 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 4 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 9 maximum + + RC channel 5 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 5 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 9 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 9 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 10 minimum + + RC channel 5 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 10 trim - Mid point value (has to be set to the same as min for throttle channel). - 800.0 - 2200.0 - us - modules/sensors - - - RC channel 10 maximum - Maximum value for this channel. - 1500.0 - 2200.0 - us - modules/sensors - - - RC channel 10 reverse + + RC channel 5 reverse Set to -1 to reverse channel. -1.0 1.0 modules/sensors + + Reverse + Normal + - - RC channel 10 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 11 minimum - Minimum value for this channel. - 800.0 - 1500.0 - us - modules/sensors - - - RC channel 11 trim + + RC channel 5 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 11 maximum + + RC channel 6 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 6 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 11 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 11 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 12 minimum + + RC channel 6 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 12 trim + + RC channel 6 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 6 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 12 maximum + + RC channel 7 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 7 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 12 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 12 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 13 minimum + + RC channel 7 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 13 trim + + RC channel 7 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 7 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 13 maximum + + RC channel 8 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 8 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 13 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 13 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 14 minimum + + RC channel 8 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 14 trim + + RC channel 8 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 8 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 14 maximum + + RC channel 9 dead zone + The +- range of this value around the trim value will be considered as zero. + 0.0 + 100.0 + modules/sensors + + + RC channel 9 maximum Maximum value for this channel. 1500.0 2200.0 us modules/sensors - - RC channel 14 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 14 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 15 minimum + + RC channel 9 minimum Minimum value for this channel. 800.0 1500.0 us modules/sensors - - RC channel 15 trim + + RC channel 9 reverse + Set to -1 to reverse channel. + -1.0 + 1.0 + modules/sensors + + Reverse + Normal + + + + RC channel 9 trim Mid point value (has to be set to the same as min for throttle channel). 800.0 2200.0 us modules/sensors - - RC channel 15 maximum - Maximum value for this channel. - 1500.0 - 2200.0 + + RC channel count + This parameter is used by Ground Station software to save the number of channels which were used during RC calibration. It is only meant for ground station use. + 0 + 18 + modules/sensors + + + Failsafe channel PWM threshold + Set to a value slightly above the PWM value assumed by throttle in a failsafe event, but ensure it is below the PWM value assumed by throttle during normal operation. + 0 + 2200 us modules/sensors - - RC channel 15 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 + + Cutoff frequency for the low pass filter on roll, pitch, yaw and throttle + Does not get set unless below RC_FLT_SMP_RATE/2 because of filter instability characteristics. Set to 0 to disable the filter. + 0 + Hz modules/sensors - - RC channel 15 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 + + Sample rate of the remote control values for the low pass filter on roll, pitch, yaw and throttle + Has an influence on the cutoff frequency precision. + 1.0 + Hz modules/sensors - - RC channel 16 minimum - Minimum value for this channel. - 800.0 - 1500.0 - us - modules/sensors - - - RC channel 16 trim - Mid point value (has to be set to the same as min for throttle channel). - 800.0 - 2200.0 - us - modules/sensors - - - RC channel 16 maximum - Maximum value for this channel. - 1500.0 - 2200.0 - us - modules/sensors - - - RC channel 16 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 16 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 17 minimum - Minimum value for this channel. - 800.0 - 1500.0 - us - modules/sensors - - - RC channel 17 trim - Mid point value (has to be set to the same as min for throttle channel). - 800.0 - 2200.0 - us - modules/sensors - - - RC channel 17 maximum - Maximum value for this channel. - 1500.0 - 2200.0 - us - modules/sensors - - - RC channel 17 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 17 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - RC channel 18 minimum - Minimum value for this channel. - 800.0 - 1500.0 - us - modules/sensors - - - RC channel 18 trim - Mid point value (has to be set to the same as min for throttle channel). - 800.0 - 2200.0 - us - modules/sensors - - - RC channel 18 maximum - Maximum value for this channel. - 1500.0 - 2200.0 - us - modules/sensors - - - RC channel 18 reverse - Set to -1 to reverse channel. - -1.0 - 1.0 - modules/sensors - - - RC channel 18 dead zone - The +- range of this value around the trim value will be considered as zero. - 0.0 - 100.0 - modules/sensors - - - Relay control of relay 1 mapped to the Spektrum receiver power supply + + AUX1 Passthrough RC channel + Default function: Camera pitch 0 - 1 + 18 modules/sensors - Relay controls DSM power - Disabled + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - RC channel count - This parameter is used by Ground Station software to save the number of channels which were used during RC calibration. It is only meant for ground station use. - 0 - 18 - modules/sensors - - - RC mode switch threshold automatic distribution - This parameter is used by Ground Station software to specify whether the threshold values for flight mode switches were automatically calculated. 0 indicates that the threshold values were set by the user. Any other value indicates that the threshold value where automatically set by the ground station software. It is only meant for ground station use. - - modules/sensors - - - Roll control channel mapping - The channel index (starting from 1 for channel 1) indicates which channel should be used for reading roll inputs from. A value of zero indicates the switch is not assigned. + + AUX2 Passthrough RC channel + Default function: Camera roll 0 18 modules/sensors - Channel 11 + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 + + + + AUX3 Passthrough RC channel + Default function: Camera azimuth / yaw + 0 + 18 + modules/sensors + Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Pitch control channel mapping - The channel index (starting from 1 for channel 1) indicates which channel should be used for reading pitch inputs from. A value of zero indicates the switch is not assigned. + + AUX4 Passthrough RC channel 0 18 modules/sensors - Channel 11 + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 + + + + AUX5 Passthrough RC channel + 0 + 18 + modules/sensors + Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 @@ -6604,329 +6517,267 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 - - - - Throttle control channel mapping - The channel index (starting from 1 for channel 1) indicates which channel should be used for reading throttle inputs from. A value of zero indicates the switch is not assigned. - 0 - 18 - modules/sensors - - Channel 11 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 - - Yaw control channel mapping - The channel index (starting from 1 for channel 1) indicates which channel should be used for reading yaw inputs from. A value of zero indicates the switch is not assigned. + + PARAM1 tuning channel + Can be used for parameter tuning with the RC. This one is further referenced as the 1st parameter channel. Set to 0 to deactivate * 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - AUX1 Passthrough RC channel - Default function: Camera pitch + + PARAM2 tuning channel + Can be used for parameter tuning with the RC. This one is further referenced as the 2nd parameter channel. Set to 0 to deactivate * 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - AUX2 Passthrough RC channel - Default function: Camera roll + + PARAM3 tuning channel + Can be used for parameter tuning with the RC. This one is further referenced as the 3th parameter channel. Set to 0 to deactivate * 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - AUX3 Passthrough RC channel - Default function: Camera azimuth / yaw + + Pitch control channel mapping + The channel index (starting from 1 for channel 1) indicates which channel should be used for reading pitch inputs from. A value of zero indicates the switch is not assigned. 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - AUX4 Passthrough RC channel + + Roll control channel mapping + The channel index (starting from 1 for channel 1) indicates which channel should be used for reading roll inputs from. A value of zero indicates the switch is not assigned. 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - AUX5 Passthrough RC channel + + Throttle control channel mapping + The channel index (starting from 1 for channel 1) indicates which channel should be used for reading throttle inputs from. A value of zero indicates the switch is not assigned. 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - PARAM1 tuning channel - Can be used for parameter tuning with the RC. This one is further referenced as the 1st parameter channel. Set to 0 to deactivate * + + Yaw control channel mapping + The channel index (starting from 1 for channel 1) indicates which channel should be used for reading yaw inputs from. A value of zero indicates the switch is not assigned. 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 - - - - PARAM2 tuning channel - Can be used for parameter tuning with the RC. This one is further referenced as the 2nd parameter channel. Set to 0 to deactivate * - 0 - 18 - modules/sensors - - Channel 11 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 - - PARAM3 tuning channel - Can be used for parameter tuning with the RC. This one is further referenced as the 3th parameter channel. Set to 0 to deactivate * + + PWM input channel that provides RSSI + 0: do not read RSSI from input channel 1-18: read RSSI from specified input channel Specify the range for RSSI input with RC_RSSI_PWM_MIN and RC_RSSI_PWM_MAX parameters. 0 18 - modules/sensors + drivers/px4io - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Failsafe channel PWM threshold - Set to a value slightly above the PWM value assumed by throttle in a failsafe event, but ensure it is below the PWM value assumed by throttle during normal operation. + + Max input value for RSSI reading + Only used if RC_RSSI_PWM_CHAN > 0 0 - 2200 - us - modules/sensors - - - Sample rate of the remote control values for the low pass filter on roll,pitch, yaw and throttle - Has an influence on the cutoff frequency precision. - 1.0 - Hz - modules/sensors + 2000 + drivers/px4io - - Cutoff frequency for the low pass filter on roll,pitch, yaw and throttle - Does not get set unless below RC_FLT_SMP_RATE/2 because of filter instability characteristics. - 0.1 - Hz - modules/sensors + + Min input value for RSSI reading + Only used if RC_RSSI_PWM_CHAN > 0 + 0 + 2000 + drivers/px4io - - Roll trim + + Pitch trim The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. -0.25 0.25 @@ -6934,8 +6785,8 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 0.01 modules/commander - - Pitch trim + + Roll trim The trim value is the actuator control value the system needs for straight and level flight. It can be calibrated by flying manually straight and level using the RC trims and copying them using the GCS. -0.25 0.25 @@ -6953,153 +6804,224 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit modules/commander - - - Loiter Time - The amount of time in seconds the system should loiter at current position before termination Set to -1 to make the system skip loitering - -1.0 - s - 1 - 0.1 - modules/navigator - - - - Single channel flight mode selection - If this parameter is non-zero, flight modes are only selected by this channel and are assigned to six slots. + + Threshold for selecting acro mode + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for the arm switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for selecting assist mode + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for selecting auto mode + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for the landing gear switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for the kill switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for selecting loiter mode + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Threshold for the manual switch + 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th + -1 + 1 + modules/sensors + + + Acro switch channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Mode switch channel mapping - This is the main flight mode selector. The channel index (starting from 1 for channel 1) indicates which channel should be used for deciding about the main mode. A value of zero indicates the switch is not assigned. + + Arm switch channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Return switch channel + + Flaps channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Rattitude switch channel + + Single channel flight mode selection + If this parameter is non-zero, flight modes are only selected by this channel and are assigned to six slots. 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Position Control switch channel + + Landing gear switch channel 0 18 modules/sensors - Channel 11 + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 + + + + Kill switch channel + 0 + 18 + modules/sensors + Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 @@ -7108,52 +7030,80 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Acro switch channel + + Manual switch channel mapping 0 18 modules/sensors - Channel 11 + Unassigned + Channel 1 + Channel 2 + Channel 3 + Channel 4 + Channel 5 + Channel 6 + Channel 7 + Channel 8 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 + + + + Mode switch channel mapping + This is the main flight mode selector. The channel index (starting from 1 for channel 1) indicates which channel should be used for deciding about the main mode. A value of zero indicates the switch is not assigned. + 0 + 18 + modules/sensors + Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 @@ -7162,160 +7112,106 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Kill switch channel + + Position Control switch channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Arm switch channel + + Rattitude switch channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 - - - - Flaps channel - 0 - 18 - modules/sensors - - Channel 11 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 - - VTOL transition switch channel mapping + + Return switch channel 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 - - - - Landing gear switch channel - 0 - 18 - modules/sensors - - Channel 11 + Channel 9 Channel 10 - Channel 13 + Channel 11 Channel 12 - Channel 15 + Channel 13 Channel 14 - Channel 17 + Channel 15 Channel 16 + Channel 17 Channel 18 - Channel 1 - Unassigned - Channel 3 - Channel 2 - Channel 5 - Channel 4 - Channel 7 - Channel 6 - Channel 9 - Channel 8 @@ -7324,63 +7220,63 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Manual switch channel mapping + + VTOL transition switch channel mapping 0 18 modules/sensors - Channel 11 - Channel 10 - Channel 13 - Channel 12 - Channel 15 - Channel 14 - Channel 17 - Channel 16 - Channel 18 - Channel 1 Unassigned - Channel 3 + Channel 1 Channel 2 - Channel 5 + Channel 3 Channel 4 - Channel 7 + Channel 5 Channel 6 - Channel 9 + Channel 7 Channel 8 + Channel 9 + Channel 10 + Channel 11 + Channel 12 + Channel 13 + Channel 14 + Channel 15 + Channel 16 + Channel 17 + Channel 18 - - Threshold for selecting assist mode + + Threshold for selecting offboard mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th -1 1 modules/sensors - - Threshold for selecting auto mode + + Threshold for selecting posctl mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th -1 1 @@ -7393,13 +7289,6 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 1 modules/sensors - - Threshold for selecting posctl mode - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - Threshold for selecting return to launch mode 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th @@ -7407,55 +7296,6 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 1 modules/sensors - - Threshold for selecting loiter mode - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for selecting acro mode - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for selecting offboard mode - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for the kill switch - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for the arm switch - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for the VTOL transition switch - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - - - Threshold for the landing gear switch - 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th - -1 - 1 - modules/sensors - Threshold for the stabilize switch 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th @@ -7463,8 +7303,8 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 1 modules/sensors - - Threshold for the manual switch + + Threshold for the VTOL transition switch 0-1 indicate where in the full channel range the threshold sits 0 : min 1 : max sign indicates polarity of comparison positive : true when channel>th negative : true when channel<th -1 1 @@ -7472,16 +7312,6 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit - - RTL altitude - Altitude to fly back in RTL in meters - 0 - 150 - m - 1 - 0.5 - modules/navigator - RTL loiter altitude Stay at this altitude above home position after RTL descending. Land (i.e. slowly descend) from this altitude if autolanding allowed. @@ -7502,6 +7332,15 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 0.5 modules/navigator + + RTL land location + Land at the home location or planned mission landing + modules/navigator + + Home Position + Planned Landing (Mission) + + Minimum distance to trigger rising to a safe altitude If the system is horizontally closer than this distance to home it will land straight on home instead of raising to the return altitude first. @@ -7512,35 +7351,61 @@ towards MPC_ACC_HOR_MAX/MPC_ACC_UP_MAX with jerk limit 0.5 modules/navigator + + RTL altitude + Altitude to fly back in RTL in meters + 0 + 150 + m + 1 + 0.5 + modules/navigator + - - Runway takeoff with landing gear - - lib/runway_takeoff + + Min. airspeed scaling factor for takeoff. +Pitch up will be commanded when the following airspeed is reached: +FW_AIRSPD_MIN * RWTO_AIRSPD_SCL + 0.0 + 2.0 + norm + 2 + 0.01 + modules/fw_pos_control_l1/runway_takeoff Specifies which heading should be held during runnway takeoff 0: airframe heading, 1: heading towards takeoff waypoint 0 1 - lib/runway_takeoff + modules/fw_pos_control_l1/runway_takeoff - Waypoint Airframe + Waypoint - - Altitude AGL at which we have enough ground clearance to allow some roll. -Until RWTO_NAV_ALT is reached the plane is held level and only -rudder is used to keep the heading (see RWTO_HDG). This should be below -FW_CLMBOUT_DIFF if FW_CLMBOUT_DIFF > 0 + + Max pitch during takeoff. +Fixed-wing settings are used if set to 0. Note that there is also a minimum +pitch of 10 degrees during takeoff, so this must be larger if set 0.0 - 100.0 - m - 1 - 1 - lib/runway_takeoff + 60.0 + deg + 1 + 0.5 + modules/fw_pos_control_l1/runway_takeoff + + + Max roll during climbout. +Roll is limited during climbout to ensure enough lift and prevents aggressive +navigation before we're on a safe height + 0.0 + 60.0 + deg + 1 + 0.5 + modules/fw_pos_control_l1/runway_takeoff Max throttle during runway takeoff. @@ -7550,7 +7415,19 @@ FW_CLMBOUT_DIFF if FW_CLMBOUT_DIFF > 0 norm 2 0.01 - lib/runway_takeoff + modules/fw_pos_control_l1/runway_takeoff + + + Altitude AGL at which we have enough ground clearance to allow some roll. +Until RWTO_NAV_ALT is reached the plane is held level and only +rudder is used to keep the heading (see RWTO_HDG). This should be below +FW_CLMBOUT_DIFF if FW_CLMBOUT_DIFF > 0 + 0.0 + 100.0 + m + 1 + 1 + modules/fw_pos_control_l1/runway_takeoff Pitch setpoint during taxi / before takeoff airspeed is reached. @@ -7562,51 +7439,41 @@ to takeoff is reached deg 1 0.5 - lib/runway_takeoff - - - Max pitch during takeoff. -Fixed-wing settings are used if set to 0. Note that there is also a minimum -pitch of 10 degrees during takeoff, so this must be larger if set - 0.0 - 60.0 - deg - 1 - 0.5 - lib/runway_takeoff - - - Max roll during climbout. -Roll is limited during climbout to ensure enough lift and prevents aggressive -navigation before we're on a safe height - 0.0 - 60.0 - deg - 1 - 0.5 - lib/runway_takeoff + modules/fw_pos_control_l1/runway_takeoff - - Min. airspeed scaling factor for takeoff. -Pitch up will be commanded when the following airspeed is reached: -FW_AIRSPD_MIN * RWTO_AIRSPD_SCL - 0.0 - 2.0 - norm - 2 - 0.01 - lib/runway_takeoff + + Runway takeoff with landing gear + + modules/fw_pos_control_l1/runway_takeoff - - UTC offset (unit: min) - the difference in hours and minutes from Coordinated Universal Time (UTC) for a your place and date. for example, In case of South Korea(UTC+09:00), UTC offset is 540 min (9*60) refer to https://en.wikipedia.org/wiki/List_of_UTC_time_offsets - -1000 + + Maximum number of log directories to keep + If there are more log directories than this value, the system will delete the oldest directories during startup. In addition, the system will delete old logs if there is not enough free space left. The minimum amount is 300 MB. If this is set to 0, old directories will only be removed if the free space falls below the minimum. + 0 1000 - min + true modules/logger + + Extended logging mode + A value of -1 indicates the command line argument should be obeyed. A value of 0 disables extended logging mode, a value of 1 enables it. This parameter is only read out before logging starts (which commonly is before arming). + -1 + 1 + modules/sdlog2 + + Command Line + Disable + Enable + + + + Use timestamps only if GPS 3D fix is available + Constrain the log folder creation to only use the time stamp if a 3D GPS lock is present. + + modules/sdlog2 + Logging Mode Determines when to start and stop logging. By default, logging is started when arming the system, and stopped when disarming. This parameter is only for the new logger (SYS_LOGGER=1). @@ -7615,11 +7482,25 @@ FW_AIRSPD_MIN * RWTO_AIRSPD_SCL true modules/logger - from boot until disarm when armed until disarm (default) + from boot until disarm from boot until shutdown + + Give logging app higher thread priority to avoid data loss. +This is used for gathering replay logs for the ekf2 module + A value of 0 indicates that the default priority is used. Increasing the parameter in steps of one increases the priority. + 0 + 3 + modules/sdlog2 + + Low priority + Default priority + Medium priority + Max priority + + Logging Topic Profile This is an integer bitmask controlling the set and rates of logged topics. The default allows for general log analysis and estimator replay, while keeping the log file size reasonably small. Enabling multiple sets leads to higher bandwidth requirements and larger log files. Set bits in the following positions to enable: 0 : Set to true to use the default set (used for general log analysis) 1 : Set to true to enable full rate estimator (EKF2) replay topics 2 : Set to true to enable topics for thermal calibration (high rate raw IMU and Baro sensor data) 3 : Set to true to enable topics for system identification (high rate actuator control and IMU data) 4 : Set to true to enable full rates for analysis of fast maneuvers (RC, attitude, rates and actuators) 5 : Set to true to enable debugging topics (debug_*.msg topics, for custom code) 6 : Set to true to enable topics for sensor comparison (low rate raw IMU, Baro and Magnetomer data) @@ -7637,20 +7518,6 @@ FW_AIRSPD_MIN * RWTO_AIRSPD_SCL sensor comparison - - Maximum number of log directories to keep - If there are more log directories than this value, the system will delete the oldest directories during startup. In addition, the system will delete old logs if there is not enough free space left. The minimum amount is 300 MB. If this is set to 0, old directories will only be removed if the free space falls below the minimum. - 0 - 1000 - true - modules/logger - - - Log UUID - If set to 1, add an ID to the log, which uniquely identifies the vehicle - - modules/logger - Logging rate A value of -1 indicates the commandline argument should be obeyed. A value of 0 sets the minimum rate, any other value is interpreted as rate in Hertz. This parameter is only read out before logging starts (which commonly is before arming). @@ -7659,44 +7526,22 @@ FW_AIRSPD_MIN * RWTO_AIRSPD_SCL Hz modules/sdlog2 - - Extended logging mode - A value of -1 indicates the command line argument should be obeyed. A value of 0 disables extended logging mode, a value of 1 enables it. This parameter is only read out before logging starts (which commonly is before arming). - -1 - 1 - modules/sdlog2 - - Enable - Disable - Command Line - + + UTC offset (unit: min) + the difference in hours and minutes from Coordinated Universal Time (UTC) for a your place and date. for example, In case of South Korea(UTC+09:00), UTC offset is 540 min (9*60) refer to https://en.wikipedia.org/wiki/List_of_UTC_time_offsets + -1000 + 1000 + min + modules/logger - - Use timestamps only if GPS 3D fix is available - Constrain the log folder creation to only use the time stamp if a 3D GPS lock is present. + + Log UUID + If set to 1, add an ID to the log, which uniquely identifies the vehicle - modules/sdlog2 - - - Give logging app higher thread priority to avoid data loss. -This is used for gathering replay logs for the ekf2 module - A value of 0 indicates that the default priority is used. Increasing the parameter in steps of one increases the priority. - 0 - 3 - modules/sdlog2 - - Default priority - Low priority - Max priority - Medium priority - + modules/logger - - Simulator UDP port - modules/simulator - Simulator Battery drain interval 1 @@ -7705,146 +7550,142 @@ This is used for gathering replay logs for the ekf2 module 1 modules/simulator + + Simulator UDP port + modules/simulator + - - ID of the Accelerometer that the calibration is for - modules/sensors - - - Accelerometer 1 enabled + + Accelerometer 0 enabled modules/sensors - - Accelerometer X-axis offset + + ID of the Accelerometer that the calibration is for modules/sensors - - Accelerometer Y-axis offset + + Accelerometer X-axis offset modules/sensors - - Accelerometer Z-axis offset + + Accelerometer X-axis scaling factor modules/sensors - - Accelerometer X-axis scaling factor + + Accelerometer Y-axis offset modules/sensors - + Accelerometer Y-axis scaling factor modules/sensors - - Accelerometer Z-axis scaling factor + + Accelerometer Z-axis offset modules/sensors - - ID of Magnetometer the calibration is for + + Accelerometer Z-axis scaling factor modules/sensors - - Mag 3 enabled + + Accelerometer 1 enabled modules/sensors - - Rotation of magnetometer 2 relative to airframe - An internal magnetometer will force a value of -1, so a GCS should only attempt to configure the rotation if the value is greater than or equal to zero. - -1 - 30 - true - modules/sensors - - Pitch 90° - Pitch 270° - Roll 270° - Roll 270°, Yaw 45° - Roll 270°, Yaw 90° - Roll 270°, Yaw 135° - Yaw 45° - No rotation - Yaw 135° - Yaw 90° - Yaw 225° - Yaw 180° - Yaw 315° - Yaw 270° - Roll 180°, Yaw 45° - Roll 180° - Roll 180°, Yaw 135° - Roll 180°, Yaw 90° - Roll 180°, Yaw 225° - Pitch 180° - Roll 180°, Yaw 315° - Roll 180°, Yaw 270° - Roll 90°, Yaw 45° - Roll 90° - Roll 90°, Yaw 135° - Roll 90°, Yaw 90° - Internal mag - - - - Magnetometer X-axis offset + + ID of the Accelerometer that the calibration is for modules/sensors - - Magnetometer Y-axis offset + + Accelerometer X-axis offset modules/sensors - - Magnetometer Z-axis offset + + Accelerometer X-axis scaling factor modules/sensors - - Magnetometer X-axis scaling factor + + Accelerometer Y-axis offset modules/sensors - - Magnetometer Y-axis scaling factor + + Accelerometer Y-axis scaling factor modules/sensors - - Magnetometer Z-axis scaling factor + + Accelerometer Z-axis offset modules/sensors - - ID of the Accelerometer that the calibration is for + + Accelerometer Z-axis scaling factor modules/sensors - + Accelerometer 2 enabled modules/sensors - - Accelerometer X-axis offset + + ID of the Accelerometer that the calibration is for modules/sensors - - Accelerometer Y-axis offset + + Accelerometer X-axis offset modules/sensors - - Accelerometer Z-axis offset + + Accelerometer X-axis scaling factor modules/sensors - - Accelerometer X-axis scaling factor + + Accelerometer Y-axis offset modules/sensors - + Accelerometer Y-axis scaling factor modules/sensors - + + Accelerometer Z-axis offset + modules/sensors + + Accelerometer Z-axis scaling factor modules/sensors - - ID of the Gyro that the calibration is for + + Primary accel ID + modules/sensors + + + Airspeed sensor compensation model for the SDP3x + Model with Pitot CAL_AIR_TUBED_MM: Not used, 1.5 mm tubes assumed. CAL_AIR_TUBELEN: Length of the tubes connecting the pitot to the sensor. Model without Pitot (1.5 mm tubes) CAL_AIR_TUBED_MM: Not used, 1.5 mm tubes assumed. CAL_AIR_TUBELEN: Length of the tubes connecting the pitot to the sensor. Tube Pressure Drop CAL_AIR_TUBED_MM: Diameter in mm of the pitot and tubes, must have the same diameter. CAL_AIR_TUBELEN: Length of the tubes connecting the pitot to the sensor and the static + dynamic port length of the pitot. + modules/sensors + + Model with Pitot + Model without Pitot (1.5 mm tubes) + Tube Pressure Drop + + + + Airspeed sensor tube diameter. Only used for the Tube Pressure Drop Compensation + 0.1 + 100 + millimeter + modules/sensors + + + Airspeed sensor tube length + See the CAL_AIR_CMODEL explanation on how this parameter should be set. + 0.01 + 2.00 + meter + modules/sensors + + + Primary baro ID modules/sensors @@ -7852,1441 +7693,1529 @@ This is used for gathering replay logs for the ekf2 module modules/sensors + + ID of the Gyro that the calibration is for + modules/sensors + Gyro X-axis offset modules/sensors + + Gyro X-axis scaling factor + modules/sensors + Gyro Y-axis offset modules/sensors + + Gyro Y-axis scaling factor + modules/sensors + Gyro Z-axis offset modules/sensors - + + Gyro Z-axis scaling factor + modules/sensors + + + Gyro 1 enabled + + modules/sensors + + + ID of the Gyro that the calibration is for + modules/sensors + + + Gyro X-axis offset + modules/sensors + + Gyro X-axis scaling factor modules/sensors - + + Gyro Y-axis offset + modules/sensors + + Gyro Y-axis scaling factor modules/sensors - + + Gyro Z-axis offset + modules/sensors + + Gyro Z-axis scaling factor modules/sensors - - Primary baro ID + + Gyro 2 enabled + modules/sensors - - Airspeed sensor pitot model + + ID of the Gyro that the calibration is for modules/sensors - - HB Pitot - - - Airspeed sensor tube length - 0.01 - 0.5 - meter + + Gyro X-axis offset modules/sensors - - Differential pressure sensor offset - The offset (zero-reading) in Pascal + + Gyro X-axis scaling factor modules/sensors - - Differential pressure sensor analog scaling - Pick the appropriate scaling from the datasheet. this number defines the (linear) conversion from voltage to Pascal (pa). For the MPXV7002DP this is 1000. NOTE: If the sensor always registers zero, try switching the static and dynamic tubes. + + Gyro Y-axis offset modules/sensors - - ID of Magnetometer the calibration is for + + Gyro Y-axis scaling factor modules/sensors - - Mag 1 enabled + + Gyro Z-axis offset + modules/sensors + + + Gyro Z-axis scaling factor + modules/sensors + + + Primary gyro ID + modules/sensors + + + Mag 0 enabled modules/sensors - - Rotation of magnetometer 1 relative to airframe + + ID of Magnetometer the calibration is for + modules/sensors + + + Rotation of magnetometer 0 relative to airframe An internal magnetometer will force a value of -1, so a GCS should only attempt to configure the rotation if the value is greater than or equal to zero. -1 30 true modules/sensors - Pitch 90° - Pitch 270° - Roll 270° - Roll 270°, Yaw 45° - Roll 270°, Yaw 90° - Roll 270°, Yaw 135° - Yaw 45° + Internal mag No rotation - Yaw 135° + Yaw 45° Yaw 90° - Yaw 225° + Yaw 135° Yaw 180° - Yaw 315° + Yaw 225° Yaw 270° - Roll 180°, Yaw 45° + Yaw 315° Roll 180° - Roll 180°, Yaw 135° + Roll 180°, Yaw 45° Roll 180°, Yaw 90° - Roll 180°, Yaw 225° + Roll 180°, Yaw 135° Pitch 180° - Roll 180°, Yaw 315° + Roll 180°, Yaw 225° Roll 180°, Yaw 270° - Roll 90°, Yaw 45° + Roll 180°, Yaw 315° Roll 90° - Roll 90°, Yaw 135° + Roll 90°, Yaw 45° Roll 90°, Yaw 90° - Internal mag + Roll 90°, Yaw 135° + Roll 270° + Roll 270°, Yaw 45° + Roll 270°, Yaw 90° + Roll 270°, Yaw 135° + Pitch 90° + Pitch 270° - + Magnetometer X-axis offset modules/sensors - - Magnetometer Y-axis offset + + Magnetometer X-axis scaling factor modules/sensors - - Magnetometer Z-axis offset + + Magnetometer Y-axis offset modules/sensors - - Magnetometer X-axis scaling factor + + Magnetometer Y-axis scaling factor modules/sensors - - Magnetometer Y-axis scaling factor + + Magnetometer Z-axis offset modules/sensors - + Magnetometer Z-axis scaling factor modules/sensors - - ID of Magnetometer the calibration is for + + Mag 1 enabled + modules/sensors - - Mag 2 enabled - + + ID of Magnetometer the calibration is for modules/sensors - - Rotation of magnetometer 2 relative to airframe + + Rotation of magnetometer 1 relative to airframe An internal magnetometer will force a value of -1, so a GCS should only attempt to configure the rotation if the value is greater than or equal to zero. -1 30 true modules/sensors - Pitch 90° - Pitch 270° - Roll 270° - Roll 270°, Yaw 45° - Roll 270°, Yaw 90° - Roll 270°, Yaw 135° - Yaw 45° + Internal mag No rotation - Yaw 135° + Yaw 45° Yaw 90° - Yaw 225° + Yaw 135° Yaw 180° - Yaw 315° + Yaw 225° Yaw 270° - Roll 180°, Yaw 45° + Yaw 315° Roll 180° - Roll 180°, Yaw 135° + Roll 180°, Yaw 45° Roll 180°, Yaw 90° - Roll 180°, Yaw 225° + Roll 180°, Yaw 135° Pitch 180° - Roll 180°, Yaw 315° + Roll 180°, Yaw 225° Roll 180°, Yaw 270° - Roll 90°, Yaw 45° + Roll 180°, Yaw 315° Roll 90° - Roll 90°, Yaw 135° + Roll 90°, Yaw 45° Roll 90°, Yaw 90° - Internal mag + Roll 90°, Yaw 135° + Roll 270° + Roll 270°, Yaw 45° + Roll 270°, Yaw 90° + Roll 270°, Yaw 135° + Pitch 90° + Pitch 270° - + Magnetometer X-axis offset modules/sensors - - Magnetometer Y-axis offset - modules/sensors - - - Magnetometer Z-axis offset - modules/sensors - - + Magnetometer X-axis scaling factor modules/sensors - - Magnetometer Y-axis scaling factor + + Magnetometer Y-axis offset modules/sensors - - Magnetometer Z-axis scaling factor + + Magnetometer Y-axis scaling factor modules/sensors - - Primary mag ID + + Magnetometer Z-axis offset modules/sensors - - ID of the Gyro that the calibration is for + + Magnetometer Z-axis scaling factor modules/sensors - - Gyro 2 enabled + + Mag 2 enabled modules/sensors - - Gyro X-axis offset + + ID of Magnetometer the calibration is for modules/sensors - - Gyro Y-axis offset - modules/sensors - - - Gyro Z-axis offset - modules/sensors - - - Gyro X-axis scaling factor - modules/sensors - - - Gyro Y-axis scaling factor - modules/sensors - - - Gyro Z-axis scaling factor - modules/sensors - - - ID of Magnetometer the calibration is for - modules/sensors - - - Mag 0 enabled - - modules/sensors - - - Rotation of magnetometer 0 relative to airframe + + Rotation of magnetometer 2 relative to airframe An internal magnetometer will force a value of -1, so a GCS should only attempt to configure the rotation if the value is greater than or equal to zero. -1 30 true modules/sensors - Pitch 90° - Pitch 270° - Roll 270° - Roll 270°, Yaw 45° - Roll 270°, Yaw 90° - Roll 270°, Yaw 135° - Yaw 45° + Internal mag No rotation - Yaw 135° + Yaw 45° Yaw 90° - Yaw 225° + Yaw 135° Yaw 180° - Yaw 315° + Yaw 225° Yaw 270° - Roll 180°, Yaw 45° + Yaw 315° Roll 180° - Roll 180°, Yaw 135° + Roll 180°, Yaw 45° Roll 180°, Yaw 90° - Roll 180°, Yaw 225° + Roll 180°, Yaw 135° Pitch 180° - Roll 180°, Yaw 315° + Roll 180°, Yaw 225° Roll 180°, Yaw 270° - Roll 90°, Yaw 45° + Roll 180°, Yaw 315° Roll 90° - Roll 90°, Yaw 135° + Roll 90°, Yaw 45° Roll 90°, Yaw 90° - Internal mag + Roll 90°, Yaw 135° + Roll 270° + Roll 270°, Yaw 45° + Roll 270°, Yaw 90° + Roll 270°, Yaw 135° + Pitch 90° + Pitch 270° - + Magnetometer X-axis offset modules/sensors - - Magnetometer Y-axis offset - modules/sensors - - - Magnetometer Z-axis offset + + Magnetometer X-axis scaling factor modules/sensors - - Magnetometer X-axis scaling factor + + Magnetometer Y-axis offset modules/sensors - + Magnetometer Y-axis scaling factor modules/sensors - - Magnetometer Z-axis scaling factor + + Magnetometer Z-axis offset modules/sensors - - ID of the Gyro that the calibration is for + + Magnetometer Z-axis scaling factor modules/sensors - - Gyro 1 enabled + + Mag 3 enabled modules/sensors - - Gyro X-axis offset + + ID of Magnetometer the calibration is for modules/sensors - - Gyro Y-axis offset + + Rotation of magnetometer 2 relative to airframe + An internal magnetometer will force a value of -1, so a GCS should only attempt to configure the rotation if the value is greater than or equal to zero. + -1 + 30 + true modules/sensors + + Internal mag + No rotation + Yaw 45° + Yaw 90° + Yaw 135° + Yaw 180° + Yaw 225° + Yaw 270° + Yaw 315° + Roll 180° + Roll 180°, Yaw 45° + Roll 180°, Yaw 90° + Roll 180°, Yaw 135° + Pitch 180° + Roll 180°, Yaw 225° + Roll 180°, Yaw 270° + Roll 180°, Yaw 315° + Roll 90° + Roll 90°, Yaw 45° + Roll 90°, Yaw 90° + Roll 90°, Yaw 135° + Roll 270° + Roll 270°, Yaw 45° + Roll 270°, Yaw 90° + Roll 270°, Yaw 135° + Pitch 90° + Pitch 270° + - - Gyro Z-axis offset + + Magnetometer X-axis offset modules/sensors - - Gyro X-axis scaling factor + + Magnetometer X-axis scaling factor modules/sensors - - Gyro Y-axis scaling factor + + Magnetometer Y-axis offset modules/sensors - - Gyro Z-axis scaling factor + + Magnetometer Y-axis scaling factor modules/sensors - - Primary accel ID + + Magnetometer Z-axis offset modules/sensors - - Primary gyro ID + + Magnetometer Z-axis scaling factor modules/sensors - - ID of the Accelerometer that the calibration is for + + Primary mag ID modules/sensors - - Accelerometer 0 enabled - + + Differential pressure sensor analog scaling + Pick the appropriate scaling from the datasheet. this number defines the (linear) conversion from voltage to Pascal (pa). For the MPXV7002DP this is 1000. NOTE: If the sensor always registers zero, try switching the static and dynamic tubes. modules/sensors - - Accelerometer X-axis offset + + Differential pressure sensor offset + The offset (zero-reading) in Pascal modules/sensors - - Accelerometer Y-axis offset + + Optical Flow minimum focus distance + This parameter defines the minimum distance from ground required for the optical flow sensor to operate reliably. The sensor may be usable below this height, but accuracy will progressively reduce to loss of focus. * modules/sensors - - Accelerometer Z-axis offset + + + + Bitfield selecting mag sides for calibration + DETECT_ORIENTATION_TAIL_DOWN = 1 DETECT_ORIENTATION_NOSE_DOWN = 2 DETECT_ORIENTATION_LEFT = 4 DETECT_ORIENTATION_RIGHT = 8 DETECT_ORIENTATION_UPSIDE_DOWN = 16 DETECT_ORIENTATION_RIGHTSIDE_UP = 32 + 34 + 63 modules/sensors + + Two side calibration + Three side calibration + Six side calibration + - - Accelerometer X-axis scaling factor + + Driver level cutoff frequency for accel + The cutoff frequency for the 2nd order butterworth filter on the accel driver. This features is currently supported by the mpu6000 and mpu9250. This only affects the signal sent to the controllers, not the estimators. 0 disables the filter. + 0 + 1000 + Hz + true modules/sensors - - Accelerometer Y-axis scaling factor + + Driver level cutoff frequency for gyro + The cutoff frequency for the 2nd order butterworth filter on the gyro driver. This features is currently supported by the mpu6000 and mpu9250. This only affects the signal sent to the controllers, not the estimators. 0 disables the filter. + 0 + 1000 + Hz + true modules/sensors - - Accelerometer Z-axis scaling factor + + QNH for barometer + 500 + 1500 + hPa modules/sensors - - - - Set to 1 to enable thermal compensation for accelerometer sensors. Set to 0 to disable - 0 - 1 + + Board rotation + This parameter defines the rotation of the FMU board relative to the platform. + true modules/sensors + + No rotation + Yaw 45° + Yaw 90° + Yaw 135° + Yaw 180° + Yaw 225° + Yaw 270° + Yaw 315° + Roll 180° + Roll 180°, Yaw 45° + Roll 180°, Yaw 90° + Roll 180°, Yaw 135° + Pitch 180° + Roll 180°, Yaw 225° + Roll 180°, Yaw 270° + Roll 180°, Yaw 315° + Roll 90° + Roll 90°, Yaw 45° + Roll 90°, Yaw 90° + Roll 90°, Yaw 135° + Roll 270° + Roll 270°, Yaw 45° + Roll 270°, Yaw 90° + Roll 270°, Yaw 135° + Pitch 90° + Pitch 270° + Roll 270°, Yaw 270° + Roll 180°, Pitch 270° + Pitch 90°, Yaw 180 + Pitch 90°, Roll 90° + Yaw 293°, Pitch 68°, Roll 90° (Solo) + Pitch 90°, Roll 270° + Pitch 9°, Yaw 180° + Pitch 45° + Pitch 315° + - - ID of Accelerometer that the calibration is for + + Board rotation X (Roll) offset + This parameter defines a rotational offset in degrees around the X (Roll) axis It allows the user to fine tune the board offset in the event of misalignment. + deg modules/sensors - - Accelerometer offset temperature ^3 polynomial coefficient - X axis + + Board rotation Y (Pitch) offset + This parameter defines a rotational offset in degrees around the Y (Pitch) axis. It allows the user to fine tune the board offset in the event of misalignment. + deg modules/sensors - - Accelerometer offset temperature ^3 polynomial coefficient - Y axis + + Board rotation Z (YAW) offset + This parameter defines a rotational offset in degrees around the Z (Yaw) axis. It allows the user to fine tune the board offset in the event of misalignment. + deg modules/sensors - - Accelerometer offset temperature ^3 polynomial coefficient - Z axis - modules/sensors - - - Accelerometer offset temperature ^2 polynomial coefficient - X axis - modules/sensors - - - Accelerometer offset temperature ^2 polynomial coefficient - Y axis - modules/sensors + + LeddarOne rangefinder + + true + drivers/distance_sensor/leddar_one - - Accelerometer offset temperature ^2 polynomial coefficient - Z axis - modules/sensors + + Lidar-Lite (LL40LS) + 0 + 2 + true + drivers/distance_sensor/ll40ls + + Disabled + PWM + I2C + - - Accelerometer offset temperature ^1 polynomial coefficient - X axis - modules/sensors + + Maxbotix Soanr (mb12xx) + + true + drivers/distance_sensor/mb12xx - - Accelerometer offset temperature ^1 polynomial coefficient - Y axis - modules/sensors + + Lightware laser rangefinder (serial) + 0 + 4 + true + drivers/distance_sensor/sf0x + + Disabled + SF02 + SF10/a + SF10/b + SF10/c + SF11/c + - - Accelerometer offset temperature ^1 polynomial coefficient - Z axis - modules/sensors + + Lightware SF1xx/SF20/LW20 laser rangefinder (i2c) + 0 + 5 + true + drivers/distance_sensor/sf1xx + + Disabled + SF10/a + SF10/b + SF10/c + SF11/c + SF/LW20 + - - Accelerometer offset temperature ^0 polynomial coefficient - X axis - modules/sensors + + Benewake TFmini laser rangefinder + + true + drivers/distance_sensor/tfmini - - Accelerometer offset temperature ^0 polynomial coefficient - Y axis + + Thermal control of sensor temperature modules/sensors + + Thermal control unavailable + Thermal control off + - - Accelerometer offset temperature ^0 polynomial coefficient - Z axis - modules/sensors + + TeraRanger Rangefinder (i2c) + 0 + 3 + true + drivers/distance_sensor/teraranger + + Disabled + Autodetect + TROne + TREvo + - - Accelerometer scale factor - X axis + + PX4Flow board rotation + This parameter defines the yaw rotation of the PX4FLOW board relative to the vehicle body frame. Zero rotation is defined as X on flow board pointing towards front of vehicle. The recommneded installation default for the PX4FLOW board is with the Y axis forward (270 deg yaw). + true modules/sensors + + No rotation + Yaw 45° + Yaw 90° + Yaw 135° + Yaw 180° + Yaw 225° + Yaw 270° + Yaw 315° + - - Accelerometer scale factor - Y axis - modules/sensors + + + + ESC UART baud rate + Default rate is 250Kbps, whic is used in off-the-shelf QRP ESC products. + platforms/qurt/fc_addon/uart_esc + + + ESC model + See esc_model_t enum definition in uart_esc_dev.h for all supported ESC model enum values. + platforms/qurt/fc_addon/uart_esc + + ESC_200QX + ESC_350QX + ESC_210QC + - - Accelerometer scale factor - Z axis - modules/sensors + + Motor 1 Mapping + platforms/qurt/fc_addon/uart_esc - - Accelerometer calibration reference temperature - modules/sensors + + Motor 2 Mapping + platforms/qurt/fc_addon/uart_esc - - Accelerometer calibration minimum temperature - modules/sensors + + Motor 3 Mapping + platforms/qurt/fc_addon/uart_esc - - Accelerometer calibration maximum temperature - modules/sensors + + Motor 4 Mapping + platforms/qurt/fc_addon/uart_esc - - ID of Accelerometer that the calibration is for - modules/sensors + + + + Interval of one subscriber in the example in ms + ms + examples/subscriber - - Accelerometer offset temperature ^3 polynomial coefficient - X axis - modules/sensors + + Float Demonstration Parameter in the Example + examples/subscriber - - Accelerometer offset temperature ^3 polynomial coefficient - Y axis - modules/sensors + + + + Operating address of the NRF51 (most significant byte) + modules/syslink - - Accelerometer offset temperature ^3 polynomial coefficient - Z axis - modules/sensors + + Operating address of the NRF51 (least significant 4 bytes) + modules/syslink - - Accelerometer offset temperature ^2 polynomial coefficient - X axis - modules/sensors + + Operating channel of the NRF51 + 0 + 125 + modules/syslink - - Accelerometer offset temperature ^2 polynomial coefficient - Y axis - modules/sensors + + Operating datarate of the NRF51 + 0 + 2 + modules/syslink - - Accelerometer offset temperature ^2 polynomial coefficient - Z axis - modules/sensors + + + + RGB Led brightness limit + Set to 0 to disable, 1 for minimum brightness up to 15 (max) + 0 + 15 + drivers/rgbled - - Accelerometer offset temperature ^1 polynomial coefficient - X axis - modules/sensors + + Automatically configure default values + Set to 1 to reset parameters on next system startup (setting defaults). Platform-specific values are used if available. RC* parameters are preserved. + 0 + 1 + modules/systemlib + + Keep parameters + Reset parameters + - - Accelerometer offset temperature ^1 polynomial coefficient - Y axis - modules/sensors + + Auto-start script index + CHANGING THIS VALUE REQUIRES A RESTART. Defines the auto-start script used to bootstrap the system. + 0 + 99999 + true + modules/systemlib - - Accelerometer offset temperature ^1 polynomial coefficient - Z axis - modules/sensors + + Enable auto start of accelerometer thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) + 0 + 1 + modules/systemlib - - Accelerometer offset temperature ^0 polynomial coefficient - X axis - modules/sensors + + Enable auto start of barometer thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) + 0 + 1 + modules/systemlib - - Accelerometer offset temperature ^0 polynomial coefficient - Y axis - modules/sensors + + Enable auto start of rate gyro thermal calibration at the next power up + 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) + 0 + 1 + modules/systemlib - - Accelerometer offset temperature ^0 polynomial coefficient - Z axis - modules/sensors + + Required temperature rise during thermal calibration + A temperature increase greater than this value is required during calibration. Calibration will complete for each sensor when the temperature increase above the starting temeprature exceeds the value set by SYS_CAL_TDEL. If the temperature rise is insufficient, the calibration will continue indefinitely and the board will need to be repowered to exit. + 10 + deg C + modules/systemlib - - Accelerometer scale factor - X axis - modules/sensors + + Maximum starting temperature for thermal calibration + Temperature calibration will not start if the temperature of any sensor is higher than the value set by SYS_CAL_TMAX. + deg C + modules/systemlib - - Accelerometer scale factor - Y axis - modules/sensors + + Minimum starting temperature for thermal calibration + Temperature calibration for each sensor will ignore data if the temperature is lower than the value set by SYS_CAL_TMIN. + deg C + modules/systemlib - - Accelerometer scale factor - Z axis - modules/sensors + + TELEM2 as companion computer link + CHANGING THIS VALUE REQUIRES A RESTART. Configures the baud rate of the TELEM2 connector as companion computer interface. + 0 + 1921600 + true + modules/systemlib + + Disabled + FrSky Telemetry + Crazyflie (Syslink) + Companion Link (57600 baud, 8N1) + OSD (57600 baud, 8N1) + Command Receiver (57600 baud, 8N1) + Normal Telemetry (19200 baud, 8N1) + Normal Telemetry (38400 baud, 8N1) + Normal Telemetry (57600 baud, 8N1) + Iridium Telemetry (19200 baud, 8N1) + Minimal Telemetry (19200 baud, 8N1) + Minimal Telemetry (38400 baud, 8N1) + Minimal Telemetry (57600 baud, 8N1) + Companion Link (921600 baud, 8N1) + ESP8266 (921600 baud, 8N1) + Normal Telemetry (115200 baud, 8N1) + Minimal Telemetry (115200 baud, 8N1) + - - Accelerometer calibration reference temperature - modules/sensors + + Run the FMU as a task to reduce latency + If true, the FMU will run in a separate task instead of on the work queue. Set this if low latency is required, for example for racing. This is a trade-off between RAM usage and latency: running as a task, it requires a separate stack and directly polls on the control topics, whereas running on the work queue, it runs at a fixed update rate. + + true + drivers/px4fmu - - Accelerometer calibration minimum temperature - modules/sensors + + Enable HITL mode on next boot + While enabled the system will boot in HITL mode and not enable all sensors and checks. When disabled the same vehicle can be normally flown outdoors. + + true + modules/systemlib - - Accelerometer calibration maximum temperature - modules/sensors + + SD logger + 0 + 1 + true + modules/systemlib + + sdlog2 (legacy) + logger (default) + - - ID of Accelerometer that the calibration is for - modules/sensors + + Set multicopter estimator group + Set the group of estimators used for multicopters and VTOLs + 1 + 2 + true + modules/systemlib + + local_position_estimator, attitude_estimator_q + ekf2 + - - Accelerometer offset temperature ^3 polynomial coefficient - X axis - modules/sensors + + Parameter version + This monotonically increasing number encodes the parameter compatibility set. whenever it increases parameters might not be backwards compatible and ground control stations should suggest a fresh configuration. + 0 + modules/systemlib - - Accelerometer offset temperature ^3 polynomial coefficient - Y axis - modules/sensors + + Set restart type + Set by px4io to indicate type of restart + 0 + 2 + modules/systemlib + + Data survives resets + Data survives in-flight resets only + Data does not survive reset + - - Accelerometer offset temperature ^3 polynomial coefficient - Z axis - modules/sensors + + Enable stack checking + + modules/systemlib - - Accelerometer offset temperature ^2 polynomial coefficient - X axis - modules/sensors + + Set usage of IO board + Can be used to use a standard startup script but with a FMU only set-up. Set to 0 to force the FMU only set-up. + 0 + 1 + + true + drivers/px4io - - Accelerometer offset temperature ^2 polynomial coefficient - Y axis - modules/sensors + + + + TEST_1 + systemcmds/tests - - Accelerometer offset temperature ^2 polynomial coefficient - Z axis - modules/sensors + + TEST_2 + systemcmds/tests - - Accelerometer offset temperature ^1 polynomial coefficient - X axis - modules/sensors + + TEST_3 + systemcmds/tests - - Accelerometer offset temperature ^1 polynomial coefficient - Y axis - modules/sensors + + TEST_D + lib/controllib/controllib_test - - Accelerometer offset temperature ^1 polynomial coefficient - Z axis - modules/sensors + + TEST_DEV + lib/controllib/controllib_test - - Accelerometer offset temperature ^0 polynomial coefficient - X axis - modules/sensors + + TEST_D_LP + lib/controllib/controllib_test - - Accelerometer offset temperature ^0 polynomial coefficient - Y axis - modules/sensors + + TEST_HP + lib/controllib/controllib_test - - Accelerometer offset temperature ^0 polynomial coefficient - Z axis + + TEST_I + lib/controllib/controllib_test + + + TEST_I_MAX + lib/controllib/controllib_test + + + TEST_LP + lib/controllib/controllib_test + + + TEST_MAX + lib/controllib/controllib_test + + + TEST_MEAN + lib/controllib/controllib_test + + + TEST_MIN + lib/controllib/controllib_test + + + TEST_P + lib/controllib/controllib_test + + + TEST_PARAMS + systemcmds/tests + + + TEST_RC2_X + systemcmds/tests + + + TEST_RC_X + systemcmds/tests + + + TEST_TRIM + lib/controllib/controllib_test + + + + + ID of Accelerometer that the calibration is for modules/sensors - + Accelerometer scale factor - X axis modules/sensors - + Accelerometer scale factor - Y axis modules/sensors - + Accelerometer scale factor - Z axis modules/sensors - - Accelerometer calibration reference temperature + + Accelerometer calibration maximum temperature modules/sensors - + Accelerometer calibration minimum temperature modules/sensors - - Accelerometer calibration maximum temperature + + Accelerometer calibration reference temperature modules/sensors - - Set to 1 to enable thermal compensation for rate gyro sensors. Set to 0 to disable - 0 - 1 + + Accelerometer offset temperature ^0 polynomial coefficient - X axis modules/sensors - - ID of Gyro that the calibration is for + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - X axis + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Y axis + + Accelerometer offset temperature ^1 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Z axis + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - X axis + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Y axis + + Accelerometer offset temperature ^2 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Z axis + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - X axis + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Y axis + + Accelerometer offset temperature ^3 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Z axis - modules/sensors - - - Gyro rate offset temperature ^0 polynomial coefficient - X axis + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Y axis + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Z axis + + ID of Accelerometer that the calibration is for modules/sensors - - Gyro scale factor - X axis + + Accelerometer scale factor - X axis modules/sensors - - Gyro scale factor - Y axis + + Accelerometer scale factor - Y axis modules/sensors - - Gyro scale factor - Z axis + + Accelerometer scale factor - Z axis modules/sensors - - Gyro calibration reference temperature + + Accelerometer calibration maximum temperature modules/sensors - - Gyro calibration minimum temperature + + Accelerometer calibration minimum temperature modules/sensors - - Gyro calibration maximum temperature + + Accelerometer calibration reference temperature modules/sensors - - ID of Gyro that the calibration is for + + Accelerometer offset temperature ^0 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - X axis + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Y axis + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Z axis + + Accelerometer offset temperature ^1 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - X axis + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Y axis + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Z axis + + Accelerometer offset temperature ^2 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - X axis + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Y axis + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Z axis + + Accelerometer offset temperature ^3 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - X axis + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Y axis + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Z axis + + ID of Accelerometer that the calibration is for modules/sensors - - Gyro scale factor - X axis + + Accelerometer scale factor - X axis modules/sensors - - Gyro scale factor - Y axis + + Accelerometer scale factor - Y axis modules/sensors - - Gyro scale factor - Z axis + + Accelerometer scale factor - Z axis modules/sensors - - Gyro calibration reference temperature + + Accelerometer calibration maximum temperature modules/sensors - - Gyro calibration minimum temperature + + Accelerometer calibration minimum temperature modules/sensors - - Gyro calibration maximum temperature + + Accelerometer calibration reference temperature modules/sensors - - ID of Gyro that the calibration is for + + Accelerometer offset temperature ^0 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - X axis + + Accelerometer offset temperature ^0 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Y axis + + Accelerometer offset temperature ^0 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^3 polynomial coefficient - Z axis + + Accelerometer offset temperature ^1 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - X axis + + Accelerometer offset temperature ^1 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Y axis + + Accelerometer offset temperature ^1 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^2 polynomial coefficient - Z axis + + Accelerometer offset temperature ^2 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - X axis + + Accelerometer offset temperature ^2 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Y axis + + Accelerometer offset temperature ^2 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^1 polynomial coefficient - Z axis + + Accelerometer offset temperature ^3 polynomial coefficient - X axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - X axis + + Accelerometer offset temperature ^3 polynomial coefficient - Y axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Y axis + + Accelerometer offset temperature ^3 polynomial coefficient - Z axis modules/sensors - - Gyro rate offset temperature ^0 polynomial coefficient - Z axis + + Thermal compensation for accelerometer sensors + 0 + 1 + modules/sensors - - Gyro scale factor - X axis + + ID of Barometer that the calibration is for modules/sensors - - Gyro scale factor - Y axis + + Barometer scale factor - X axis modules/sensors - - Gyro scale factor - Z axis + + Barometer calibration maximum temperature modules/sensors - - Gyro calibration reference temperature + + Barometer calibration minimum temperature modules/sensors - - Gyro calibration minimum temperature + + Barometer calibration reference temperature modules/sensors - - Gyro calibration maximum temperature + + Barometer offset temperature ^0 polynomial coefficient modules/sensors - - Set to 1 to enable thermal compensation for barometric pressure sensors. Set to 0 to disable - 0 - 1 + + Barometer offset temperature ^1 polynomial coefficients modules/sensors - - ID of Barometer that the calibration is for + + Barometer offset temperature ^2 polynomial coefficient modules/sensors - - Barometer offset temperature ^5 polynomial coefficient + + Barometer offset temperature ^3 polynomial coefficient modules/sensors Barometer offset temperature ^4 polynomial coefficient modules/sensors - - Barometer offset temperature ^3 polynomial coefficient + + Barometer offset temperature ^5 polynomial coefficient modules/sensors - - Barometer offset temperature ^2 polynomial coefficient + + ID of Barometer that the calibration is for modules/sensors - - Barometer offset temperature ^1 polynomial coefficients + + Barometer scale factor - X axis modules/sensors - - Barometer offset temperature ^0 polynomial coefficient + + Barometer calibration maximum temperature modules/sensors - - Barometer scale factor - X axis + + Barometer calibration minimum temperature modules/sensors - + Barometer calibration reference temperature modules/sensors - - Barometer calibration minimum temperature + + Barometer offset temperature ^0 polynomial coefficient modules/sensors - - Barometer calibration maximum temperature + + Barometer offset temperature ^1 polynomial coefficients modules/sensors - - ID of Barometer that the calibration is for + + Barometer offset temperature ^2 polynomial coefficient modules/sensors - - Barometer offset temperature ^5 polynomial coefficient + + Barometer offset temperature ^3 polynomial coefficient modules/sensors Barometer offset temperature ^4 polynomial coefficient modules/sensors - - Barometer offset temperature ^3 polynomial coefficient + + Barometer offset temperature ^5 polynomial coefficient modules/sensors - - Barometer offset temperature ^2 polynomial coefficient + + ID of Barometer that the calibration is for modules/sensors - - Barometer offset temperature ^1 polynomial coefficients - modules/sensors + + Barometer scale factor - X axis + modules/sensors - - Barometer offset temperature ^0 polynomial coefficient + + Barometer calibration maximum temperature modules/sensors - - Barometer scale factor - X axis + + Barometer calibration minimum temperature modules/sensors - + Barometer calibration reference temperature modules/sensors - - Barometer calibration minimum temperature + + Barometer offset temperature ^0 polynomial coefficient modules/sensors - - Barometer calibration maximum temperature + + Barometer offset temperature ^1 polynomial coefficients modules/sensors - - ID of Barometer that the calibration is for + + Barometer offset temperature ^2 polynomial coefficient modules/sensors - - Barometer offset temperature ^5 polynomial coefficient + + Barometer offset temperature ^3 polynomial coefficient modules/sensors Barometer offset temperature ^4 polynomial coefficient modules/sensors - - Barometer offset temperature ^3 polynomial coefficient + + Barometer offset temperature ^5 polynomial coefficient modules/sensors - - Barometer offset temperature ^2 polynomial coefficient + + Thermal compensation for barometric pressure sensors + 0 + 1 + modules/sensors - - Barometer offset temperature ^1 polynomial coefficients + + ID of Gyro that the calibration is for modules/sensors - - Barometer offset temperature ^0 polynomial coefficient + + Gyro scale factor - X axis modules/sensors - - Barometer scale factor - X axis + + Gyro scale factor - Y axis modules/sensors - - Barometer calibration reference temperature + + Gyro scale factor - Z axis modules/sensors - - Barometer calibration minimum temperature + + Gyro calibration maximum temperature modules/sensors - - Barometer calibration maximum temperature + + Gyro calibration minimum temperature modules/sensors - - - - QNH for barometer - 500 - 1500 - hPa + + Gyro calibration reference temperature modules/sensors - - Board rotation - This parameter defines the rotation of the FMU board relative to the platform. - true + + Gyro rate offset temperature ^0 polynomial coefficient - X axis modules/sensors - - Pitch 90° - Pitch 270° - Roll 270° - Roll 270°, Yaw 45° - Roll 270°, Yaw 90° - Roll 270°, Yaw 135° - Yaw 45° - No rotation - Yaw 135° - Yaw 90° - Yaw 225° - Yaw 180° - Yaw 315° - Yaw 270° - Roll 180°, Yaw 45° - Roll 180° - Roll 180°, Yaw 135° - Roll 180°, Yaw 90° - Roll 180°, Yaw 225° - Pitch 180° - Roll 180°, Yaw 315° - Roll 180°, Yaw 270° - Roll 90°, Yaw 45° - Roll 90° - Roll 90°, Yaw 135° - Roll 90°, Yaw 90° - - - PX4Flow board rotation - This parameter defines the yaw rotation of the PX4FLOW board relative to the vehicle body frame. Zero rotation is defined as X on flow board pointing towards front of vehicle. The recommneded installation default for the PX4FLOW board is with the Y axis forward (270 deg yaw). - true + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis modules/sensors - - Yaw 45° - No rotation - Yaw 135° - Yaw 90° - Yaw 225° - Yaw 180° - Yaw 315° - Yaw 270° - - - Board rotation Y (Pitch) offset - This parameter defines a rotational offset in degrees around the Y (Pitch) axis. It allows the user to fine tune the board offset in the event of misalignment. - deg + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis modules/sensors - - Board rotation X (Roll) offset - This parameter defines a rotational offset in degrees around the X (Roll) axis It allows the user to fine tune the board offset in the event of misalignment. - deg + + Gyro rate offset temperature ^1 polynomial coefficient - X axis modules/sensors - - Board rotation Z (YAW) offset - This parameter defines a rotational offset in degrees around the Z (Yaw) axis. It allows the user to fine tune the board offset in the event of misalignment. - deg + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis modules/sensors - - Threshold (of RMS) to warn about high vibration levels - 0.01 - 10 - 2 + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis modules/sensors - - Lidar-Lite (LL40LS) - 0 - 2 - true + + Gyro rate offset temperature ^2 polynomial coefficient - X axis modules/sensors - - PWM - Disabled - I2C - - - Lightware laser rangefinder (serial) - 0 - 4 - true + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis modules/sensors - - SF02 - Disabled - SF10/b - SF10/a - SF11/c - SF10/c - - - Maxbotix Soanr (mb12xx) - - true + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis modules/sensors - - TeraRanger Rangefinder (i2c) - 0 - 3 - true + + Gyro rate offset temperature ^3 polynomial coefficient - X axis modules/sensors - - Autodetect - Disabled - TREvo - TROne - - - Lightware SF1xx/SF20/LW20 laser rangefinder (i2c) - 0 - 5 - true + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis modules/sensors - - SF10/a - Disabled - SF10/c - SF10/b - SF/LW20 - SF11/c - - - Thermal control of sensor temperature + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis modules/sensors - - Thermal control off - Thermal control unavailable - - - Driver level cut frequency for gyro - The cut frequency for the 2nd order butterworth filter on the gyro driver. This features is currently supported by the mpu6000 and mpu9250. This only affects the signal sent to the controllers, not the estimators. 0 disables the filter. - 5 - 1000 - Hz - true + + ID of Gyro that the calibration is for modules/sensors - - Driver level cut frequency for accel - The cut frequency for the 2nd order butterworth filter on the accel driver. This features is currently supported by the mpu6000 and mpu9250. This only affects the signal sent to the controllers, not the estimators. 0 disables the filter. - 5 - 1000 - Hz - true + + Gyro scale factor - X axis modules/sensors - - Bitfield selecting mag sides for calibration - DETECT_ORIENTATION_TAIL_DOWN = 1 DETECT_ORIENTATION_NOSE_DOWN = 2 DETECT_ORIENTATION_LEFT = 4 DETECT_ORIENTATION_RIGHT = 8 DETECT_ORIENTATION_UPSIDE_DOWN = 16 DETECT_ORIENTATION_RIGHTSIDE_UP = 32 - 34 - 63 + + Gyro scale factor - Y axis modules/sensors - - Two side calibration - Six side calibration - Three side calibration - - - - - Interval of one subscriber in the example in ms - ms - examples/subscriber + + Gyro scale factor - Z axis + modules/sensors - - Float Demonstration Parameter in the Example - examples/subscriber + + Gyro calibration maximum temperature + modules/sensors - - - - Operating channel of the NRF51 - 0 - 125 - modules/syslink - - - Operating datarate of the NRF51 - 0 - 2 - modules/syslink - - - Operating address of the NRF51 (most significant byte) - modules/syslink - - - Operating address of the NRF51 (least significant 4 bytes) - modules/syslink - - - - - Run the FMU as a task to reduce latency - If true, the FMU will run in a separate task instead of on the work queue. Set this if low latency is required, for example for racing. This is a trade-off between RAM usage and latency: running as a task, it requires a separate stack and directly polls on the control topics, whereas running on the work queue, it runs at a fixed update rate. - - true - drivers/px4fmu - - - Set usage of IO board - Can be used to use a standard startup script but with a FMU only set-up. Set to 0 to force the FMU only set-up. - 0 - 1 - - true - drivers/px4io + + Gyro calibration minimum temperature + modules/sensors - - RGB Led brightness limit - Set to 0 to disable, 1 for minimum brightness up to 15 (max) - 0 - 15 - drivers/rgbled + + Gyro calibration reference temperature + modules/sensors - - Auto-start script index - CHANGING THIS VALUE REQUIRES A RESTART. Defines the auto-start script used to bootstrap the system. - 0 - 99999 - true - modules/systemlib + + Gyro rate offset temperature ^0 polynomial coefficient - X axis + modules/sensors - - Automatically configure default values - Set to 1 to reset parameters on next system startup (setting defaults). Platform-specific values are used if available. RC* parameters are preserved. - 0 - 1 - modules/systemlib - - Reset parameters - Keep parameters - + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis + modules/sensors - - Enable HITL mode on next boot - While enabled the system will boot in HITL mode and not enable all sensors and checks. When disabled the same vehicle can be normally flown outdoors. - - true - modules/systemlib + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis + modules/sensors - - Set restart type - Set by px4io to indicate type of restart - 0 - 2 - modules/systemlib - - Data survives in-flight resets only - Data survives resets - Data does not survive reset - + + Gyro rate offset temperature ^1 polynomial coefficient - X axis + modules/sensors - - Set multicopter estimator group - Set the group of estimators used for multicopters and VTOLs - 1 - 2 - true - modules/systemlib - - local_position_estimator, attitude_estimator_q - ekf2 - + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis + modules/sensors - - TELEM2 as companion computer link - CHANGING THIS VALUE REQUIRES A RESTART. Configures the baud rate of the TELEM2 connector as companion computer interface. - 0 - 1921600 - true - modules/systemlib - - FrSky Telemetry - Crazyflie (Syslink) - Normal Telemetry (57600 baud, 8N1) - Command Receiver (57600 baud, 8N1) - OSD (57600 baud, 8N1) - Iridium Telemetry (19200 baud, 8N1) - Normal Telemetry (38400 baud, 8N1) - Disabled - Normal Telemetry (19200 baud, 8N1) - ESP8266 (921600 baud, 8N1) - Companion Link (57600 baud, 8N1) - Companion Link (921600 baud, 8N1) - Normal Telemetry (115200 baud, 8N1) - + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis + modules/sensors - - Parameter version - This monotonically increasing number encodes the parameter compatibility set. whenever it increases parameters might not be backwards compatible and ground control stations should suggest a fresh configuration. - 0 - modules/systemlib + + Gyro rate offset temperature ^2 polynomial coefficient - X axis + modules/sensors - - SD logger - 0 - 1 - true - modules/systemlib - - logger (default) - sdlog2 (legacy) - + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis + modules/sensors - - Enable stack checking - - modules/systemlib + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis + modules/sensors - - Enable auto start of rate gyro thermal calibration at the next power up - 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) - 0 - 1 - modules/systemlib + + Gyro rate offset temperature ^3 polynomial coefficient - X axis + modules/sensors - - Enable auto start of accelerometer thermal calibration at the next power up - 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) - 0 - 1 - modules/systemlib + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis + modules/sensors - - Enable auto start of barometer thermal calibration at the next power up - 0 : Set to 0 to do nothing 1 : Set to 1 to start a calibration at next boot This parameter is reset to zero when the the temperature calibration starts. default (0, no calibration) - 0 - 1 - modules/systemlib + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis + modules/sensors - - Required temperature rise during thermal calibration - A temperature increase greater than this value is required during calibration. Calibration will complete for each sensor when the temperature increase above the starting temeprature exceeds the value set by SYS_CAL_TDEL. If the temperature rise is insufficient, the calibration will continue indefinitely and the board will need to be repowered to exit. - 10 - deg C - modules/systemlib + + ID of Gyro that the calibration is for + modules/sensors - - Minimum starting temperature for thermal calibration - Temperature calibration for each sensor will ignore data if the temperature is lower than the value set by SYS_CAL_TMIN. - deg C - modules/systemlib + + Gyro scale factor - X axis + modules/sensors - - Maximum starting temperature for thermal calibration - Temperature calibration will not start if the temperature of any sensor is higher than the value set by SYS_CAL_TMAX. - deg C - modules/systemlib + + Gyro scale factor - Y axis + modules/sensors - - - - TEST_1 - systemcmds/tests + + Gyro scale factor - Z axis + modules/sensors - - TEST_2 - systemcmds/tests + + Gyro calibration maximum temperature + modules/sensors - - TEST_RC_X - systemcmds/tests + + Gyro calibration minimum temperature + modules/sensors - - TEST_RC2_X - systemcmds/tests + + Gyro calibration reference temperature + modules/sensors - - TEST_PARAMS - systemcmds/tests + + Gyro rate offset temperature ^0 polynomial coefficient - X axis + modules/sensors - - TEST_MIN - lib/controllib/controllib_test + + Gyro rate offset temperature ^0 polynomial coefficient - Y axis + modules/sensors - - TEST_MAX - lib/controllib/controllib_test + + Gyro rate offset temperature ^0 polynomial coefficient - Z axis + modules/sensors - - TEST_TRIM - lib/controllib/controllib_test + + Gyro rate offset temperature ^1 polynomial coefficient - X axis + modules/sensors - - TEST_HP - lib/controllib/controllib_test + + Gyro rate offset temperature ^1 polynomial coefficient - Y axis + modules/sensors - - TEST_LP - lib/controllib/controllib_test + + Gyro rate offset temperature ^1 polynomial coefficient - Z axis + modules/sensors - - TEST_P - lib/controllib/controllib_test + + Gyro rate offset temperature ^2 polynomial coefficient - X axis + modules/sensors - - TEST_I - lib/controllib/controllib_test + + Gyro rate offset temperature ^2 polynomial coefficient - Y axis + modules/sensors - - TEST_I_MAX - lib/controllib/controllib_test + + Gyro rate offset temperature ^2 polynomial coefficient - Z axis + modules/sensors - - TEST_D - lib/controllib/controllib_test + + Gyro rate offset temperature ^3 polynomial coefficient - X axis + modules/sensors - - TEST_D_LP - lib/controllib/controllib_test + + Gyro rate offset temperature ^3 polynomial coefficient - Y axis + modules/sensors - - TEST_MEAN - lib/controllib/controllib_test + + Gyro rate offset temperature ^3 polynomial coefficient - Z axis + modules/sensors - - TEST_DEV - lib/controllib/controllib_test + + Thermal compensation for rate gyro sensors + 0 + 1 + + modules/sensors - + + UAVCAN CAN bus bitrate + 20000 + 1000000 + modules/uavcannode + + UAVCAN Node ID Read the specs at http://uavcan.org to learn more about Node ID. 1 125 - modules/uavcanesc + modules/uavcannode UAVCAN CAN bus bitrate @@ -9294,18 +9223,20 @@ This is used for gathering replay logs for the ekf2 module 1000000 modules/uavcanesc - + UAVCAN Node ID Read the specs at http://uavcan.org to learn more about Node ID. 1 125 - modules/uavcannode + modules/uavcanesc - + UAVCAN CAN bus bitrate 20000 1000000 - modules/uavcannode + bit/s + true + modules/uavcan UAVCAN mode @@ -9316,10 +9247,16 @@ This is used for gathering replay logs for the ekf2 module modules/uavcan Disabled - Sensors and Motors Only Sensors + Sensors and Motors + + UAVCAN ESC will spin at idle throttle when armed, even if the mixer outputs zero setpoints + + true + modules/uavcan + UAVCAN Node ID Read the specs at http://uavcan.org to learn more about Node ID. @@ -9328,219 +9265,8 @@ This is used for gathering replay logs for the ekf2 module true modules/uavcan - - UAVCAN CAN bus bitrate - 20000 - 1000000 - bit/s - true - modules/uavcan - - - UAVCAN ESC will spin at idle throttle when armed, even if the mixer outputs zero setpoints - - true - modules/uavcan - - - Position of tilt servo in mc mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in transition mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Position of tilt servo in fw mode - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Duration of front transition phase 2 - Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. - 0.1 - 5.0 - s - 3 - 0.01 - modules/vtol_att_control - - - The channel number of motors that must be turned off in fixed wing mode - 0 - 12345678 - 0 - 1 - modules/vtol_att_control - - - Differential thrust in forwards flight - Set to 1 to enable differential thrust in fixed-wing flight. - 0 - 1 - 0 - modules/vtol_att_control - - - Differential thrust scaling factor - This factor specifies how the yaw input gets mapped to differential thrust in forwards flight. - 0.0 - 1.0 - 2 - 0.1 - modules/vtol_att_control - - - VTOL number of engines - 0 - 8 - 0 - 1 - modules/vtol_att_control - - - Idle speed of VTOL when in multicopter mode - 900 - 2000 - us - 0 - 1 - modules/vtol_att_control - - - Minimum airspeed in multicopter mode - This is the minimum speed of the air flowing over the control surfaces. - 0.0 - 30.0 - m/s - 2 - 0.5 - modules/vtol_att_control - - - Maximum airspeed in multicopter mode - This is the maximum speed of the air flowing over the control surfaces. - 0.0 - 30.0 - m/s - 2 - 0.5 - modules/vtol_att_control - - - Trim airspeed when in multicopter mode - This is the airflow over the control surfaces for which no airspeed scaling is applied in multicopter mode. - 0.0 - 30.0 - m/s - 2 - 0.5 - modules/vtol_att_control - - - Permanent stabilization in fw mode - If set to one this parameter will cause permanent attitude stabilization in fw mode. This parameter has been introduced for pure convenience sake. - - modules/vtol_att_control - - - Fixed wing pitch trim - This parameter allows to adjust the neutral elevon position in fixed wing mode. - -1.0 - 1.0 - 2 - 0.01 - modules/vtol_att_control - - - Motor max power - Indicates the maximum power the motor is able to produce. Used to calculate propeller efficiency map. - 1 - 10000 - W - 0 - 1 - modules/vtol_att_control - - - Propeller efficiency parameter - Influences propeller efficiency at different power settings. Should be tuned beforehand. - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - Total airspeed estimate low-pass filter gain - Gain for tuning the low-pass filter for the total airspeed estimate - 0.0 - 1.0 - 3 - 0.01 - modules/vtol_att_control - - - VTOL Type (Tailsitter=0, Tiltrotor=1, Standard=2) - 0 - 2 - 0 - modules/vtol_att_control - - Tiltrotor - Tailsitter - Standard - - - - Lock elevons in multicopter mode - If set to 1 the elevons are locked in multicopter mode - - modules/vtol_att_control - - - Duration of a front transition - Time in seconds used for a transition - 0.00 - 20.00 - s - 2 - 1 - modules/vtol_att_control - - - Duration of a back transition - Time in seconds used for a back transition - 0.00 - 20.00 - s - 2 - 1 - modules/vtol_att_control - - - Approximate deceleration during back transition - The approximate deceleration during a back transition in m/s/s Used to calculate back transition distance in mission mode. A lower value will make the VTOL transition further from the destination waypoint. - 0.00 - 20.00 - m/s/s - 2 - 1 - modules/vtol_att_control - Transition blending airspeed Airspeed at which we can start blending both fw and mc controls. Set to 0 to disable. @@ -9561,34 +9287,84 @@ This is used for gathering replay logs for the ekf2 module 1 modules/vtol_att_control - - Optimal recovery strategy for pitch-weak tailsitters - + + Approximate deceleration during back transition + The approximate deceleration during a back transition in m/s/s Used to calculate back transition distance in mission mode. A lower value will make the VTOL transition further from the destination waypoint. + 0.00 + 20.00 + m/s/s + 2 + 1 modules/vtol_att_control - - Front transition timeout - Time in seconds after which transition will be cancelled. Disabled if set to 0. + + Delay in seconds before applying back transition throttle +Set this to a value greater than 0 to give the motor time to spin down + unit s + 0 + 10 + 2 + 1 + modules/vtol_att_control + + + Output on airbrakes channel during back transition +Used for airbrakes or with ESCs that have reverse thrust enabled on a seperate channel +Airbrakes need to be enables for your selected model/mixer + 0 + 1 + 2 + 0.01 + modules/vtol_att_control + + + Duration of a back transition + Time in seconds used for a back transition 0.00 - 30.00 + 20.00 s 2 1 modules/vtol_att_control - - Front transition minimum time - Minimum time in seconds for front transition. + + Back transition MC motor ramp up time + This sets the duration during wich the MC motors ramp up to the commanded thrust during the back transition stage. 0.0 - 10.0 + 20.0 s modules/vtol_att_control - - QuadChute Altitude - Minimum altitude for fixed wing flight, when in fixed wing the altitude drops below this altitude the vehicle will transition back to MC mode and enter failsafe RTL + + Target throttle value for the transition to hover flight. +standard vtol: pusher +tailsitter, tiltrotor: main throttle + Note for standard vtol: For ESCs and mixers that support reverse thrust on low PWM values set this to a negative value to apply active breaking For ESCs that support thrust reversal with a control channel please set VT_B_REV_OUT and set this to a positive value to apply active breaking + -1 + 1 + 2 + 0.01 + modules/vtol_att_control + + + Maximum allowed down-pitch the controller is able to demand. This prevents large, negative +lift values being created when facing strong winds. The vehicle will use the pusher motor +to accelerate forward if necessary 0.0 - 200.0 + 45.0 + modules/vtol_att_control + + + Lock elevons in multicopter mode + If set to 1 the elevons are locked in multicopter mode + + modules/vtol_att_control + + + Fixed wing thrust scale for hover forward flight + Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. + 0.0 + 2.0 modules/vtol_att_control @@ -9598,6 +9374,44 @@ This is used for gathering replay logs for the ekf2 module 200.0 modules/vtol_att_control + + Differential thrust in forwards flight + Set to 1 to enable differential thrust in fixed-wing flight. + 0 + 1 + 0 + modules/vtol_att_control + + + Differential thrust scaling factor + This factor specifies how the yaw input gets mapped to differential thrust in forwards flight. + 0.0 + 1.0 + 2 + 0.1 + modules/vtol_att_control + + + QuadChute Altitude + Minimum altitude for fixed wing flight, when in fixed wing the altitude drops below this altitude the vehicle will transition back to MC mode and enter failsafe RTL + 0.0 + 200.0 + modules/vtol_att_control + + + The channel number of motors that must be turned off in fixed wing mode + 0 + 12345678 + 0 + 1 + modules/vtol_att_control + + + Permanent stabilization in fw mode + If set to one this parameter will cause permanent attitude stabilization in fw mode. This parameter has been introduced for pure convenience sake. + + modules/vtol_att_control + QuadChute Max Pitch Maximum pitch angle before QuadChute engages Above this the vehicle will transition back to MC mode and enter failsafe RTL @@ -9612,6 +9426,26 @@ This is used for gathering replay logs for the ekf2 module 180 modules/vtol_att_control + + Duration of a front transition + Time in seconds used for a transition + 0.00 + 20.00 + s + 2 + 1 + modules/vtol_att_control + + + Target throttle value for the transition to fixed wing flight. +standard vtol: pusher +tailsitter, tiltrotor: main throttle + 0.0 + 1.0 + 3 + 0.01 + modules/vtol_att_control + Airspeed less front transition time (open loop) The duration of the front transition when there is no airspeed feedback available. @@ -9620,73 +9454,106 @@ This is used for gathering replay logs for the ekf2 module seconds modules/vtol_att_control - - Weather-vane yaw rate scale - The desired yawrate from the controller will be scaled in order to avoid yaw fighting against the wind. - 0.0 - 1.0 - 3 + + Idle speed of VTOL when in multicopter mode + 900 + 2000 + us + 0 + 1 + modules/vtol_att_control + + + VTOL number of engines + 0 + 8 + 0 + 1 + modules/vtol_att_control + + + Optimal recovery strategy for pitch-weak tailsitters + + modules/vtol_att_control + + + Defines the time window during which the pusher throttle will be ramped up linearly to VT_F_TRANS_THR during a transition +to fixed wing mode. Zero or negative values will produce an instant throttle rise to VT_F_TRANS_THR + 20 + 2 0.01 modules/vtol_att_control - - Target throttle value for pusher/puller motor during the transition to fw mode + + Position of tilt servo in fw mode 0.0 1.0 3 0.01 modules/vtol_att_control - - Maximum allowed down-pitch the controller is able to demand. This prevents large, negative -lift values being created when facing strong winds. The vehicle will use the pusher motor -to accelerate forward if necessary + + Position of tilt servo in mc mode 0.0 - 45.0 + 1.0 + 3 + 0.01 modules/vtol_att_control - - Fixed wing thrust scale for hover forward flight - Scale applied to fixed wing thrust being used as source for forward acceleration in multirotor mode. This technique can be used to avoid the plane having to pitch down a lot in order to move forward. Setting this value to 0 (default) will disable this strategy. + + Position of tilt servo in transition mode 0.0 - 2.0 + 1.0 + 3 + 0.01 modules/vtol_att_control - - Back transition MC motor ramp up time - This sets the duration during wich the MC motors ramp up to the commanded thrust during the back transition stage. + + Front transition minimum time + Minimum time in seconds for front transition. 0.0 20.0 s modules/vtol_att_control - - Output on airbrakes channel during back transition -Used for airbrakes or with ESCs that have reverse thrust enabled on a seperate channel -Airbrakes need to be enables for your selected model/mixer - 0 - 1 - 2 + + Duration of front transition phase 2 + Time in seconds it should take for the rotors to rotate forward completely from the point when the plane has picked up enough airspeed and is ready to go into fixed wind mode. + 0.1 + 5.0 + s + 3 0.01 modules/vtol_att_control - - Delay in seconds before applying back transition throttle -Set this to a value greater than 0 to give the motor time to spin down - unit s - 0 - 10 + + Front transition timeout + Time in seconds after which transition will be cancelled. Disabled if set to 0. + 0.00 + 30.00 + s 2 1 modules/vtol_att_control - - Thottle output during back transition -For ESCs and mixers that support reverse thrust on low PWM values set this to a negative value to apply active breaking -For ESCs that support thrust reversal with a control channel please set VT_B_REV_OUT and set this to a positive value to apply active breaking - -1 - 1 - 2 + + VTOL Type (Tailsitter=0, Tiltrotor=1, Standard=2) + 0 + 2 + 0 + modules/vtol_att_control + + Tailsitter + Tiltrotor + Standard + + + + Weather-vane yaw rate scale + The desired yawrate from the controller will be scaled in order to avoid yaw fighting against the wind. + 0.0 + 1.0 + 3 0.01 modules/vtol_att_control @@ -9696,16 +9563,20 @@ For ESCs that support thrust reversal with a control channel please set VT_B_REV EXFW_HDNG_P examples/fixedwing_control + + EXFW_PITCH_P + examples/fixedwing_control + EXFW_ROLL_P examples/fixedwing_control - - EXFW_PITCH_P - examples/fixedwing_control + + RV_YAW_P + examples/rover_steering_control - - SEG_TH2V_P + + SEG_Q2V examples/segway @@ -9716,13 +9587,9 @@ For ESCs that support thrust reversal with a control channel please set VT_B_REV SEG_TH2V_I_MAX examples/segway - - SEG_Q2V + + SEG_TH2V_P examples/segway - - RV_YAW_P - examples/rover_steering_control - diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc index 9b1389273a3a43855e36264097ab825966272324..a4c3b6fc1889a75c1799f72518468986ad706611 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.cc @@ -44,11 +44,13 @@ QVariant PX4ParameterMetaData::_stringToTypedVariant(const QString& string, Fact case FactMetaData::valueTypeUint8: case FactMetaData::valueTypeUint16: case FactMetaData::valueTypeUint32: + case FactMetaData::valueTypeUint64: convertTo = QVariant::UInt; break; case FactMetaData::valueTypeInt8: case FactMetaData::valueTypeInt16: case FactMetaData::valueTypeInt32: + case FactMetaData::valueTypeInt64: convertTo = QVariant::Int; break; case FactMetaData::valueTypeFloat: @@ -181,6 +183,25 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData 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"); + } + + bool volatileValue = false; + bool readOnly = false; + QString volatileStr = xml.attributes().value("volatile").toString(); + if (volatileStr.compare(QStringLiteral("true")) == 0) { + volatileValue = true; + readOnly = true; + } + if (!volatileValue) { + QString readOnlyStr = xml.attributes().value("readonly").toString(); + if (readOnlyStr.compare(QStringLiteral("true")) == 0) { + readOnly = true; + } + } + qCDebug(PX4ParameterMetaDataLog) << "Found parameter name:" << name << " type:" << type << " default:" << strDefault; // Convert type from string to FactMetaData::ValueType_t @@ -196,7 +217,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData metaData = new FactMetaData(foundType); Q_CHECK_PTR(metaData); if (_mapParameterName2FactMetaData.contains(name)) { - // We can't trust the meta dafa since we have dups + // We can't trust the meta data since we have dups qCWarning(PX4ParameterMetaDataLog) << "Duplicate parameter found:" << name; badMetaData = true; // Reset to default meta data @@ -204,7 +225,10 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData } else { _mapParameterName2FactMetaData[name] = metaData; metaData->setName(name); + metaData->setCategory(category); metaData->setGroup(factGroup); + metaData->setReadOnly(readOnly); + metaData->setVolatileValue(volatileValue); if (xml.attributes().hasAttribute("default") && !strDefault.isEmpty()) { QVariant varDefault; @@ -243,7 +267,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qCDebug(PX4ParameterMetaDataLog) << "Min:" << text; QVariant varMin; - if (metaData->convertAndValidateRaw(text, true /* convertOnly */, varMin, errorString)) { + if (metaData->convertAndValidateRaw(text, false /* convertOnly */, varMin, errorString)) { metaData->setRawMin(varMin); } else { qCWarning(PX4ParameterMetaDataLog) << "Invalid min value, name:" << metaData->name() << " type:" << metaData->type() << " min:" << text << " error:" << errorString; @@ -254,7 +278,7 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData qCDebug(PX4ParameterMetaDataLog) << "Max:" << text; QVariant varMax; - if (metaData->convertAndValidateRaw(text, true /* convertOnly */, varMax, errorString)) { + 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; @@ -381,6 +405,17 @@ void PX4ParameterMetaData::loadParameterFactMetaDataFile(const QString& metaData } } +FactMetaData* PX4ParameterMetaData::getMetaDataForFact(const QString& name, MAV_TYPE vehicleType) +{ + Q_UNUSED(vehicleType) + + if (_mapParameterName2FactMetaData.contains(name)) { + return _mapParameterName2FactMetaData[name]; + } else { + return NULL; + } +} + void PX4ParameterMetaData::addMetaDataToFact(Fact* fact, MAV_TYPE vehicleType) { Q_UNUSED(vehicleType) diff --git a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.h b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.h index 80c7357d8e77285db5196255fcaad3c4bcb64400..e9c2bf0bb12bb4922e79beb871e1f7f6c6622b4f 100644 --- a/src/FirmwarePlugin/PX4/PX4ParameterMetaData.h +++ b/src/FirmwarePlugin/PX4/PX4ParameterMetaData.h @@ -33,8 +33,9 @@ class PX4ParameterMetaData : public QObject public: PX4ParameterMetaData(void); - void loadParameterFactMetaDataFile (const QString& metaDataFile); - void addMetaDataToFact (Fact* fact, MAV_TYPE vehicleType); + void loadParameterFactMetaDataFile (const QString& metaDataFile); + FactMetaData* getMetaDataForFact (const QString& name, MAV_TYPE vehicleType); + void addMetaDataToFact (Fact* fact, MAV_TYPE vehicleType); static void getParameterMetaDataVersionInfo(const QString& metaDataFile, int& majorVersion, int& minorVersion); diff --git a/src/FirmwarePlugin/PX4/px4_custom_mode.h b/src/FirmwarePlugin/PX4/px4_custom_mode.h index 2c0087e8f165189f89ec4d35015d7b45697fb38d..48a29f2cda456b06abc950b0b99feb6eef6641c7 100644 --- a/src/FirmwarePlugin/PX4/px4_custom_mode.h +++ b/src/FirmwarePlugin/PX4/px4_custom_mode.h @@ -71,7 +71,11 @@ union px4_custom_mode { uint8_t main_mode; uint8_t sub_mode; }; - uint32_t data; + struct { + uint16_t reserved_hl; + uint16_t custom_mode_hl; + }; + uint32_t data; float data_float; }; diff --git a/src/FlightDisplay/FlightDisplayView.qml b/src/FlightDisplay/FlightDisplayView.qml index 3c87dc13aa576040dca443faee06d60d178dd8a3..58425335678e7958c138836fb842744aa6f8fdd2 100644 --- a/src/FlightDisplay/FlightDisplayView.qml +++ b/src/FlightDisplay/FlightDisplayView.qml @@ -16,6 +16,7 @@ import QtLocation 5.3 import QtPositioning 5.3 import QtMultimedia 5.5 import QtQuick.Layouts 1.2 +import QtQuick.Window 2.2 import QGroundControl 1.0 import QGroundControl.FlightDisplay 1.0 @@ -43,8 +44,6 @@ QGCView { property var _geoFenceController: _planMasterController.geoFenceController property var _rallyPointController: _planMasterController.rallyPointController property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - property var _videoReceiver: QGroundControl.videoManager.videoReceiver - property bool _recordingVideo: _videoReceiver && _videoReceiver.recording property bool _mainIsMap: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_mainIsMapKey, true) : true property bool _isPipVisible: QGroundControl.videoManager.hasVideo ? QGroundControl.loadBoolGlobalSetting(_PIPVisibleKey, true) : false property real _savedZoomLevel: 0 @@ -94,6 +93,20 @@ QGCView { QGroundControl.saveBoolGlobalSetting(_PIPVisibleKey, state) } + function isInstrumentRight() { + if(QGroundControl.corePlugin.options.instrumentWidget) { + if(QGroundControl.corePlugin.options.instrumentWidget.source.toString().length) { + switch(QGroundControl.corePlugin.options.instrumentWidget.widgetPosition) { + case CustomInstrumentWidget.POS_TOP_LEFT: + case CustomInstrumentWidget.POS_BOTTOM_LEFT: + case CustomInstrumentWidget.POS_CENTER_LEFT: + return false; + } + } + } + return true; + } + PlanMasterController { id: masterController Component.onCompleted: start(false /* editMode */) @@ -184,6 +197,24 @@ QGCView { } } + Window { + id: videoWindow + width: !_mainIsMap ? _panel.width : _pipSize + height: !_mainIsMap ? _panel.height : _pipSize * (9/16) + visible: false + + Item { + id: videoItem + anchors.fill: parent + } + + onClosing: { + _flightVideo.state = "unpopup" + videoWindow.visible = false + } + + } + QGCMapPalette { id: mapPal; lightColors: _mainIsMap ? _flightMap.isSatelliteMap : true } QGCViewPanel { @@ -244,7 +275,11 @@ QGCView { name: "pipMode" PropertyChanges { target: _flightVideo - anchors.margins: ScreenTools.defaultFontPixelHeight + anchors.margins: ScreenTools.defaultFontPixelHeight + } + PropertyChanges { + target: _flightVideoPipControl + inPopup: false } }, State { @@ -253,10 +288,53 @@ QGCView { target: _flightVideo anchors.margins: 0 } + PropertyChanges { + target: _flightVideoPipControl + inPopup: false + } + }, + State { + name: "popup" + StateChangeScript { + script: QGroundControl.videoManager.stopVideo() + } + ParentChange { + target: _flightVideo + parent: videoItem + x: 0 + y: 0 + width: videoWindow.width + height: videoWindow.height + } + PropertyChanges { + target: _flightVideoPipControl + inPopup: true + } + }, + State { + name: "unpopup" + StateChangeScript { + script: QGroundControl.videoManager.stopVideo() + } + ParentChange { + target: _flightVideo + parent: _panel + } + PropertyChanges { + target: _flightVideo + anchors.left: _panel.left + anchors.bottom: _panel.bottom + anchors.margins: ScreenTools.defaultFontPixelHeight + } + PropertyChanges { + target: _flightVideoPipControl + inPopup: false + } } ] //-- Video Streaming FlightDisplayViewVideo { + id: videoStreaming anchors.fill: parent visible: QGroundControl.videoManager.isGStreamer } @@ -277,9 +355,10 @@ QGCView { anchors.left: _panel.left anchors.bottom: _panel.bottom anchors.margins: ScreenTools.defaultFontPixelHeight - visible: QGroundControl.videoManager.hasVideo && !QGroundControl.videoManager.fullScreen + visible: QGroundControl.videoManager.hasVideo && !QGroundControl.videoManager.fullScreen && _flightVideo.state != "popup" isHidden: !_isPipVisible isDark: isBackgroundDark + enablePopup: _mainIsMap onActivated: { _mainIsMap = !_mainIsMap setStates() @@ -287,6 +366,10 @@ QGCView { onHideIt: { setPipVisibility(!state) } + onPopup: { + videoWindow.visible = true + _flightVideo.state = "popup" + } onNewWidth: { _pipSize = newWidth } @@ -346,63 +429,6 @@ QGCView { property var qgcView: root } - // Button to start/stop video recording - Item { - z: _flightVideoPipControl.z + 1 - anchors.margins: ScreenTools.defaultFontPixelHeight / 2 - anchors.bottom: _flightVideo.bottom - anchors.right: _flightVideo.right - height: ScreenTools.defaultFontPixelHeight * 2 - width: height - visible: _videoReceiver && _videoReceiver.videoRunning && QGroundControl.settingsManager.videoSettings.showRecControl.rawValue && _flightVideo.visible && !_isCamera && !QGroundControl.videoManager.fullScreen - opacity: 0.75 - - readonly property string recordBtnBackground: "BackgroundName" - - Rectangle { - id: recordBtnBackground - anchors.top: parent.top - anchors.bottom: parent.bottom - width: height - radius: _recordingVideo ? 0 : height - color: "red" - - SequentialAnimation on visible { - running: _recordingVideo - loops: Animation.Infinite - PropertyAnimation { to: false; duration: 1000 } - PropertyAnimation { to: true; duration: 1000 } - } - } - - QGCColoredImage { - anchors.top: parent.top - anchors.bottom: parent.bottom - anchors.horizontalCenter: parent.horizontalCenter - width: height * 0.625 - sourceSize.width: width - source: "/qmlimages/CameraIcon.svg" - visible: recordBtnBackground.visible - fillMode: Image.PreserveAspectFit - color: "white" - } - - MouseArea { - anchors.fill: parent - onClicked: { - if (_videoReceiver) { - if (_recordingVideo) { - _videoReceiver.stopRecording() - // reset blinking animation - recordBtnBackground.visible = true - } else { - _videoReceiver.startRecording() - } - } - } - } - } - MultiVehicleList { anchors.margins: _margins anchors.top: singleMultiSelector.bottom @@ -434,8 +460,10 @@ QGCView { ToolStrip { visible: (_activeVehicle ? _activeVehicle.guidedModeSupported : true) && !QGroundControl.videoManager.fullScreen id: toolStrip - anchors.leftMargin: ScreenTools.defaultFontPixelWidth - anchors.left: _panel.left + anchors.leftMargin: isInstrumentRight() ? ScreenTools.defaultFontPixelWidth : undefined + anchors.left: isInstrumentRight() ? _panel.left : undefined + anchors.rightMargin:isInstrumentRight() ? undefined : ScreenTools.defaultFontPixelWidth + anchors.right: isInstrumentRight() ? undefined : _panel.right anchors.topMargin: ScreenTools.toolbarHeight + (_margins * 2) anchors.top: _panel.top z: _panel.z + 4 diff --git a/src/FlightDisplay/FlightDisplayViewMap.qml b/src/FlightDisplay/FlightDisplayViewMap.qml index df01d8b74fda9ff6ae0c5fafd7a0188c615a333b..7ae130ab8383362b83c2e7230a2147310b22c06f 100644 --- a/src/FlightDisplay/FlightDisplayViewMap.qml +++ b/src/FlightDisplay/FlightDisplayViewMap.qml @@ -143,7 +143,7 @@ FlightMap { onNewItemsFromVehicle: { var visualItems = _missionController.visualItems - if (visualItems && visualItems.count != 1) { + if (visualItems && visualItems.count !== 1) { mapFitFunctions.fitMapViewportToMissionItems() firstVehiclePositionReceived = true } diff --git a/src/FlightDisplay/FlightDisplayViewVideo.qml b/src/FlightDisplay/FlightDisplayViewVideo.qml index a3d39fd822ec9bea0fb79623ba108d0956c28810..0ed7eed06c52d2d51be399a794aa5108e788aa5d 100644 --- a/src/FlightDisplay/FlightDisplayViewVideo.qml +++ b/src/FlightDisplay/FlightDisplayViewVideo.qml @@ -34,7 +34,7 @@ Item { color: Qt.rgba(0,0,0,0.75) visible: !(_videoReceiver && _videoReceiver.videoRunning) QGCLabel { - text: qsTr("WAITING FOR VIDEO") + text: QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue ? qsTr("WAITING FOR VIDEO") : qsTr("VIDEO DISABLED") font.family: ScreenTools.demiboldFontFamily color: "white" font.pointSize: _mainIsMap ? ScreenTools.smallFontPointSize : ScreenTools.largeFontPointSize diff --git a/src/FlightDisplay/FlightDisplayViewWidgets.qml b/src/FlightDisplay/FlightDisplayViewWidgets.qml index 4caab475fca7838b6de1b5eaea18c8212f650e5b..8f4b08f9690e8f7f6f024cca8ea25c360795f81c 100644 --- a/src/FlightDisplay/FlightDisplayViewWidgets.qml +++ b/src/FlightDisplay/FlightDisplayViewWidgets.qml @@ -40,7 +40,9 @@ Item { function getPreferredInstrumentWidth() { if(ScreenTools.isMobile) { - return ScreenTools.isTinyScreen ? mainWindow.width * 0.2 : mainWindow.width * 0.15 + return mainWindow.width * 0.25 + } else if(ScreenTools.isHugeScreen) { + return mainWindow.width * 0.11 } return ScreenTools.defaultFontPixelWidth * 30 } @@ -50,15 +52,24 @@ Item { if(QGroundControl.corePlugin.options.instrumentWidget.source.toString().length) { instrumentsLoader.source = QGroundControl.corePlugin.options.instrumentWidget.source switch(QGroundControl.corePlugin.options.instrumentWidget.widgetPosition) { + case CustomInstrumentWidget.POS_TOP_LEFT: + instrumentsLoader.state = "topLeftMode" + break; + case CustomInstrumentWidget.POS_BOTTOM_LEFT: + instrumentsLoader.state = "bottomLeftMode" + break; + case CustomInstrumentWidget.POS_CENTER_LEFT: + instrumentsLoader.state = "centerLeftMode" + break; case CustomInstrumentWidget.POS_TOP_RIGHT: - instrumentsLoader.state = "topMode" + instrumentsLoader.state = "topRightMode" break; case CustomInstrumentWidget.POS_BOTTOM_RIGHT: - instrumentsLoader.state = "bottomMode" + instrumentsLoader.state = "bottomRightMode" break; case CustomInstrumentWidget.POS_CENTER_RIGHT: default: - instrumentsLoader.state = "centerMode" + instrumentsLoader.state = "centerRightMode" break; } } else { @@ -67,10 +78,9 @@ Item { var useAlternateInstruments = true//QGroundControl.settingsManager.appSettings.virtualJoystick.value || ScreenTools.isTinyScreen if(useAlternateInstruments) { instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidgetAlternate.qml" - instrumentsLoader.state = "topMode" } else { instrumentsLoader.source = "qrc:/qml/QGCInstrumentWidget.qml" - instrumentsLoader.state = QGroundControl.settingsManager.appSettings.showLargeCompass.value == 1 ? "centerMode" : "topMode" + instrumentsLoader.state = QGroundControl.settingsManager.appSettings.showLargeCompass.value === 1 ? "centerRightMode" : "topRightMode" } } } else { @@ -139,30 +149,69 @@ Item { property real maxHeight:parent.height - (anchors.margins * 2) states: [ State { - name: "topMode" + name: "topRightMode" + AnchorChanges { + target: instrumentsLoader + anchors.verticalCenter: undefined + anchors.bottom: undefined + anchors.top: _root ? _root.top : undefined + anchors.right: _root ? _root.right : undefined + anchors.left: undefined + } + }, + State { + name: "centerRightMode" + AnchorChanges { + target: instrumentsLoader + anchors.top: undefined + anchors.bottom: undefined + anchors.verticalCenter: _root ? _root.verticalCenter : undefined + anchors.right: _root ? _root.right : undefined + anchors.left: undefined + } + }, + State { + name: "bottomRightMode" + AnchorChanges { + target: instrumentsLoader + anchors.top: undefined + anchors.verticalCenter: undefined + anchors.bottom: _root ? _root.bottom : undefined + anchors.right: _root ? _root.right : undefined + anchors.left: undefined + } + }, + State { + name: "topLeftMode" AnchorChanges { target: instrumentsLoader anchors.verticalCenter: undefined anchors.bottom: undefined anchors.top: _root ? _root.top : undefined + anchors.right: undefined + anchors.left: _root ? _root.left : undefined } }, State { - name: "centerMode" + name: "centerLeftMode" AnchorChanges { target: instrumentsLoader anchors.top: undefined anchors.bottom: undefined anchors.verticalCenter: _root ? _root.verticalCenter : undefined + anchors.right: undefined + anchors.left: _root ? _root.left : undefined } }, State { - name: "bottomMode" + name: "bottomLeftMode" AnchorChanges { target: instrumentsLoader anchors.top: undefined anchors.verticalCenter: undefined anchors.bottom: _root ? _root.bottom : undefined + anchors.right: undefined + anchors.left: _root ? _root.left : undefined } } ] diff --git a/src/FlightDisplay/GuidedActionConfirm.qml b/src/FlightDisplay/GuidedActionConfirm.qml index 48c159fa2e9cf0804683f9a2d7b7a77e176f6ece..701cf0c18a7d16de26342d564316e9a906668523 100644 --- a/src/FlightDisplay/GuidedActionConfirm.qml +++ b/src/FlightDisplay/GuidedActionConfirm.qml @@ -16,7 +16,7 @@ import QGroundControl.Controls 1.0 import QGroundControl.Palette 1.0 /// Guided actions confirmation dialog -NoMouseThroughRectangle { +Rectangle { id: _root border.color: qgcPal.alertBorder border.width: 1 @@ -42,12 +42,34 @@ NoMouseThroughRectangle { if (hideTrigger) { hideTrigger = false altitudeSlider.visible = false + visibleTimer.stop() visible = false } } + function show(immediate) { + if (immediate) { + visible = true + } else { + // We delay showing the confirmation for a small amount in order to any other state + // changes to propogate through the system. This way only the final state shows up. + visibleTimer.restart() + } + } + + Timer { + id: visibleTimer + interval: 1000 + repeat: false + onTriggered: visible = true + } + QGCPalette { id: qgcPal } + DeadMouseArea { + anchors.fill: parent + } + Column { id: confirmColumn anchors.margins: _margins diff --git a/src/FlightDisplay/GuidedActionList.qml b/src/FlightDisplay/GuidedActionList.qml index 4c002c3ce1137a02353932f17ae89dd20bd53948..768c7d814f5b79a3ff4b4f01163bb81e54957729 100644 --- a/src/FlightDisplay/GuidedActionList.qml +++ b/src/FlightDisplay/GuidedActionList.qml @@ -17,7 +17,7 @@ import QGroundControl.Controls 1.0 import QGroundControl.Palette 1.0 /// Dialog showing list of available guided actions -NoMouseThroughRectangle { +Rectangle { id: _root width: actionColumn.width + (_margins * 4) height: actionColumn.height + (_margins * 4) @@ -35,6 +35,10 @@ NoMouseThroughRectangle { QGCPalette { id: qgcPal } + DeadMouseArea { + anchors.fill: parent + } + ColumnLayout { id: actionColumn anchors.margins: _root._margins diff --git a/src/FlightDisplay/GuidedActionsController.qml b/src/FlightDisplay/GuidedActionsController.qml index b704a05799ff44aa5b17a1c652f94b1c85768b79..8c5ad1556c59db935c840c158c8e27d9af6f3ccb 100644 --- a/src/FlightDisplay/GuidedActionsController.qml +++ b/src/FlightDisplay/GuidedActionsController.qml @@ -48,6 +48,7 @@ Item { readonly property string landAbortTitle: qsTr("Land Abort") readonly property string setWaypointTitle: qsTr("Set Waypoint") readonly property string gotoTitle: qsTr("Goto Location") + readonly property string vtolTransitionTitle: qsTr("VTOL Transition") readonly property string armMessage: qsTr("Arm the vehicle.") readonly property string disarmMessage: qsTr("Disarm the vehicle") @@ -67,6 +68,8 @@ Item { readonly property string landAbortMessage: qsTr("Abort the landing sequence.") readonly property string pauseMessage: qsTr("Pause the vehicle at it's current position.") readonly property string mvPauseMessage: qsTr("Pause all 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 int actionRTL: 1 readonly property int actionLand: 2 @@ -87,6 +90,8 @@ 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 property bool showEmergenyStop: !_hideEmergenyStop && _activeVehicle && _vehicleArmed && _vehicleFlying property bool showArm: _activeVehicle && !_vehicleArmed @@ -96,13 +101,15 @@ Item { property bool showLand: _activeVehicle && _activeVehicle.guidedModeSupported && _vehicleArmed && !_activeVehicle.fixedWing && !_vehicleInLandMode property bool showStartMission: _activeVehicle && _missionAvailable && !_missionActive && !_vehicleFlying property bool showContinueMission: _activeVehicle && _missionAvailable && !_missionActive && _vehicleFlying && (_currentMissionIndex < missionController.visualItems.count - 1) - property bool showResumeMission: _activeVehicle && !_vehicleArmed && _vehicleWasFlying && _missionAvailable && _resumeMissionIndex > 0 && (_resumeMissionIndex < missionController.visualItems.count - 2) property bool showPause: _activeVehicle && _vehicleArmed && _activeVehicle.pauseVehicleSupported && _vehicleFlying && !_vehiclePaused property bool showChangeAlt: (_activeVehicle && _vehicleFlying) && _activeVehicle.guidedModeSupported && _vehicleArmed && !_missionActive property bool showOrbit: !_hideOrbit && _activeVehicle && _vehicleFlying && _activeVehicle.orbitModeSupported && _vehicleArmed && !_missionActive property bool showLandAbort: _activeVehicle && _vehicleFlying && _activeVehicle.fixedWing && _vehicleLanding property bool showGotoLocation: _activeVehicle && _vehicleFlying + // Note: The 'missionController.visualItems.count - 3' is a hack to not trigger resume mission when a mission ends with an RTL item + property bool showResumeMission: _activeVehicle && !_vehicleArmed && _vehicleWasFlying && _missionAvailable && _resumeMissionIndex > 0 && (_resumeMissionIndex < missionController.visualItems.count - 3) + property bool guidedUIVisible: guidedActionConfirm.visible || guidedActionList.visible property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle @@ -178,9 +185,11 @@ Item { // Called when an action is about to be executed in order to confirm function confirmAction(actionCode, actionData) { + var showImmediate = true closeAll() confirmDialog.action = actionCode confirmDialog.actionData = actionData + confirmDialog.hideTrigger = true _actionData = actionData switch (actionCode) { case actionArm: @@ -208,10 +217,11 @@ Item { confirmDialog.title = takeoffTitle confirmDialog.message = takeoffMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showTakeoff }) - altitudeSlider.reset() + altitudeSlider.setToMinimumTakeoff() altitudeSlider.visible = true break; case actionStartMission: + showImmediate = false confirmDialog.title = startMissionTitle confirmDialog.message = startMissionMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showStartMission }) @@ -222,11 +232,13 @@ Item { confirmDialog.hideTrigger = true break; case actionContinueMission: + showImmediate = false confirmDialog.title = continueMissionTitle confirmDialog.message = continueMissionMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showContinueMission }) break; case actionResumeMission: + showImmediate = false confirmDialog.title = resumeMissionTitle confirmDialog.message = resumeMissionMessage confirmDialog.hideTrigger = Qt.binding(function() { return !showResumeMission }) @@ -287,15 +299,27 @@ 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 default: console.warn("Unknown actionCode", actionCode) return } - confirmDialog.visible = true + confirmDialog.show(showImmediate) } // Executes the specified action function executeAction(actionCode, actionData) { + var i; + var rgVehicle; switch (actionCode) { case actionRTL: _activeVehicle.guidedModeRTL() @@ -319,10 +343,9 @@ Item { _activeVehicle.startMission() break case actionMVStartMission: - var rgVehicle = QGroundControl.multiVehicleManager.vehicles - for (var i=0; i 0 ? altGainRange : altLossRange) property real newAltitudeMeters: _vehicleAltitude + altLossGain property string newAltitudeAppUnits: QGroundControl.metersToAppSettingsDistanceUnits(newAltitudeMeters).toFixed(1) + + function setToMinimumTakeoff() { + altSlider.value = Math.pow(_activeVehicle.minimumTakeoffAltitude() / altGainRange, 1.0/3.0) + } } } diff --git a/src/FlightDisplay/MultiVehicleList.qml b/src/FlightDisplay/MultiVehicleList.qml index 9a025a1a6374eb8d3a0ce63e2565507435302c6e..4c62a4f1cd7aa4a421faf7b541703c75e95967d0 100644 --- a/src/FlightDisplay/MultiVehicleList.qml +++ b/src/FlightDisplay/MultiVehicleList.qml @@ -28,7 +28,7 @@ Item { QGCPalette { id: qgcPal } - NoMouseThroughRectangle { + Rectangle { id: mvCommands anchors.left: parent.left anchors.right: parent.right @@ -37,6 +37,10 @@ Item { opacity: _rectOpacity radius: _margin + DeadMouseArea { + anchors.fill: parent + } + Column { id: mvCommandsColumn anchors.margins: _margin @@ -152,7 +156,7 @@ Item { QGCButton { text: "Start Mission" - visible: _vehicle.armed && _vehicle.flightMode != _vehicle.missionFlightMode + visible: _vehicle.armed && _vehicle.flightMode !== _vehicle.missionFlightMode onClicked: _vehicle.startMission() } @@ -164,13 +168,13 @@ Item { QGCButton { text: "RTL" - visible: _vehicle.armed && _vehicle.flightMode != _vehicle.rtlFlightMode + visible: _vehicle.armed && _vehicle.flightMode !== _vehicle.rtlFlightMode onClicked: _vehicle.flightMode = _vehicle.rtlFlightMode } QGCButton { text: "Take control" - visible: _vehicle.armed && _vehicle.flightMode != _vehicle.takeControlFlightMode + visible: _vehicle.armed && _vehicle.flightMode !== _vehicle.takeControlFlightMode onClicked: _vehicle.flightMode = _vehicle.takeControlFlightMode } } // Row diff --git a/src/FlightDisplay/VideoManager.h b/src/FlightDisplay/VideoManager.h index 2db9e8aa1bcce848161a1caa00cdf2ada4bacfa4..f419af90a281a1889feba1642979e35ca930dde4 100644 --- a/src/FlightDisplay/VideoManager.h +++ b/src/FlightDisplay/VideoManager.h @@ -56,6 +56,9 @@ public: // Override from QGCTool void setToolbox (QGCToolbox *toolbox); + Q_INVOKABLE void startVideo() {_videoReceiver->stop();}; + Q_INVOKABLE void stopVideo() {_videoReceiver->stop();}; + signals: void hasVideoChanged (); void isGStreamerChanged (); diff --git a/src/FlightDisplay/qmldir b/src/FlightDisplay/qmldir index 8968b79182d6038a964d9c91f9ecc8a8b11e9fd7..f29069e4677e831ff7a5c8015e8827c712ee2831 100644 --- a/src/FlightDisplay/qmldir +++ b/src/FlightDisplay/qmldir @@ -4,9 +4,9 @@ FlightDisplayView 1.0 FlightDisplayView.qml FlightDisplayViewMap 1.0 FlightDisplayViewMap.qml FlightDisplayViewVideo 1.0 FlightDisplayViewVideo.qml FlightDisplayViewWidgets 1.0 FlightDisplayViewWidgets.qml -GuidedActionsConfirm 1.0 GuidedActionsConfirm.qml +GuidedActionConfirm 1.0 GuidedActionConfirm.qml GuidedActionsController 1.0 GuidedActionsController.qml -GuidedActionsList 1.0 GuidedActionsList.qml +GuidedActionList 1.0 GuidedActionList.qml GuidedAltitudeSlider 1.0 GuidedAltitudeSlider.qml MultiVehicleList 1.0 MultiVehicleList.qml diff --git a/src/FlightMap/Images/PiP.svg b/src/FlightMap/Images/PiP.svg index f73f1e577fe4f979208a214e584fdc662cffae2c..84739056bd0f4f1f50ac7e5e26c0a227ab7fbe70 100644 --- a/src/FlightMap/Images/PiP.svg +++ b/src/FlightMap/Images/PiP.svg @@ -1,10 +1,63 @@ - - - - - - - - - + + + +image/svg+xml \ No newline at end of file diff --git a/src/FlightMap/Images/attitudeDial.svg b/src/FlightMap/Images/attitudeDial.svg old mode 100644 new mode 100755 index 1e74c332107021d7d1e2319c22e44d2a03132dc9..470a24a2c66fd95dcd8dcce7449aa1db59f6024c --- a/src/FlightMap/Images/attitudeDial.svg +++ b/src/FlightMap/Images/attitudeDial.svg @@ -1,15 +1,23 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + diff --git a/src/FlightMap/Images/pipHide.svg b/src/FlightMap/Images/pipHide.svg new file mode 100644 index 0000000000000000000000000000000000000000..73200628f91579bd399296ac50db57d043bde77e --- /dev/null +++ b/src/FlightMap/Images/pipHide.svg @@ -0,0 +1,84 @@ + + + + + + + image/svg+xml + + + + + + + + + + + + + diff --git a/src/FlightMap/MapItems/CustomMapItems.qml b/src/FlightMap/MapItems/CustomMapItems.qml index 11509da592675e8a14283e4e6d2ec259d42bcf7f..1df427ec2f91187677321874ba5934539c7e99a8 100644 --- a/src/FlightMap/MapItems/CustomMapItems.qml +++ b/src/FlightMap/MapItems/CustomMapItems.qml @@ -29,9 +29,9 @@ Item { Component.onCompleted: { var controlUrl = object.url - if (controlUrl != "") { + if (controlUrl !== "") { var component = Qt.createComponent(controlUrl); - if (component.status == Component.Ready) { + if (component.status === Component.Ready) { _customObject = component.createObject(map, { "customMapObject": object }) if (_customObject) { map.addMapItem(_customObject) diff --git a/src/FlightMap/MapItems/MissionItemIndicatorDrag.qml b/src/FlightMap/MapItems/MissionItemIndicatorDrag.qml index 7b9c4a12b31f3b8bf347e1bac7033eed9794aad0..720e5fd25e281c49c469b50a80d9bd75a7d264a4 100644 --- a/src/FlightMap/MapItems/MissionItemIndicatorDrag.qml +++ b/src/FlightMap/MapItems/MissionItemIndicatorDrag.qml @@ -71,11 +71,15 @@ Rectangle { drag.maximumY: itemDragger.parent.height - parent.height preventStealing: true - onClicked: itemDragger.clicked() + onClicked: { + focus = true + itemDragger.clicked() + } property bool dragActive: drag.active onDragActiveChanged: { if (dragActive) { + focus = true if (!_dragStartSignalled) { _dragStartSignalled = true dragStart() diff --git a/src/FlightMap/MapItems/PlanMapItems.qml b/src/FlightMap/MapItems/PlanMapItems.qml index 559e2d092c85e88df5ceef5e55344bd86989e92b..770b40e1303148c38b1ef552bed74c7cca6fbfa2 100644 --- a/src/FlightMap/MapItems/PlanMapItems.qml +++ b/src/FlightMap/MapItems/PlanMapItems.qml @@ -15,7 +15,8 @@ import QGroundControl 1.0 import QGroundControl.Controls 1.0 import QGroundControl.FlightMap 1.0 -// Adds visual items associated with the Flight Plan to the map +// Adds visual items associated with the Flight Plan to the map. +// Currently only used by Fly View even though it's called PlanMapItems! Item { id: _root @@ -45,24 +46,15 @@ Item { } } - // Waypoint lines - Instantiator { - model: largeMapView ? _missionController.waypointLines : 0 - - Item { - property var _missionLineViewComponent - - Component.onCompleted: { - _missionLineViewComponent = missionLineViewComponent.createObject(map, {"object": object}) - if (_missionLineViewComponent.status === Component.Error) - console.log(_missionLineViewComponent.errorString()) - map.addMapItem(_missionLineViewComponent) - } + Component.onCompleted: { + _missionLineViewComponent = missionLineViewComponent.createObject(map) + if (_missionLineViewComponent.status === Component.Error) + console.log(_missionLineViewComponent.errorString()) + map.addMapItem(_missionLineViewComponent) + } - Component.onDestruction: { - _missionLineViewComponent.destroy() - } - } + Component.onDestruction: { + _missionLineViewComponent.destroy() } Component { @@ -72,9 +64,7 @@ Item { line.width: 3 line.color: "#be781c" // Hack, can't get palette to work in here z: QGroundControl.zOrderWaypointLines - path: object ? [ object.coordinate1, object.coordinate2] : undefined - - property var object + path: _missionController.waypointPath } } } diff --git a/src/FlightMap/MapItems/PolygonEditor.qml b/src/FlightMap/MapItems/PolygonEditor.qml index 6f3631c484a166024cb5fc59b023da21358ea4de..1fa7a8dbe244c71f9d056f3953663ba76bae1b06 100644 --- a/src/FlightMap/MapItems/PolygonEditor.qml +++ b/src/FlightMap/MapItems/PolygonEditor.qml @@ -233,7 +233,7 @@ Item { var clickCoordinate = map.toCoordinate(Qt.point(mouse.x, mouse.y), false /* clipToViewPort */) var polygonPath = _newPolygon.path - if (polygonPath.length == 0) { + if (polygonPath.length === 0) { // Add first coordinate polygonPath.push(clickCoordinate) } else { @@ -252,10 +252,10 @@ Item { _currentPolygon.path = polygonPath _newPolygon.path = polygonPath - if (_mobile && _currentPolygon.path.length == 1) { + if (_mobile && _currentPolygon.path.length === 1) { _mobilePoint.coordinate = _currentPolygon.path[0] _mobilePoint.visible = true - } else if (_mobile && _currentPolygon.path.length == 2) { + } else if (_mobile && _currentPolygon.path.length === 2) { // Show initial line segment on mobile _mobileSegment.path = [ _currentPolygon.path[0], _currentPolygon.path[1] ] _mobileSegment.visible = true diff --git a/src/FlightMap/MapScale.qml b/src/FlightMap/MapScale.qml index 515d83dc6f8143cc01e045dae578d78b2d7b73da..321afc289e252ecee490387d0c3c2b8da0e683a5 100644 --- a/src/FlightMap/MapScale.qml +++ b/src/FlightMap/MapScale.qml @@ -115,7 +115,7 @@ Item { var rightCoord = mapControl.toCoordinate(Qt.point(scaleLinePixelLength, scale.y), false /* clipToViewPort */) var scaleLineMeters = Math.round(leftCoord.distanceTo(rightCoord)) - if (QGroundControl.settingsManager.unitsSettings.distanceUnits.value == UnitsSettings.DistanceUnitsFeet) { + if (QGroundControl.settingsManager.unitsSettings.distanceUnits.value === UnitsSettings.DistanceUnitsFeet) { calculateFeetRatio(scaleLineMeters, scaleLinePixelLength) } else { calculateMetersRatio(scaleLineMeters, scaleLinePixelLength) diff --git a/src/FlightMap/Widgets/CameraPageWidget.qml b/src/FlightMap/Widgets/CameraPageWidget.qml index efa6eb91d5adaeef832d27b96d0ff7c9c3849f92..672e279a2141179dfb307c35e1600fdb3bcebd69 100644 --- a/src/FlightMap/Widgets/CameraPageWidget.qml +++ b/src/FlightMap/Widgets/CameraPageWidget.qml @@ -26,23 +26,26 @@ import QGroundControl.FactControls 1.0 /// Camera page for Instrument Panel PageView Column { width: pageWidth - spacing: ScreenTools.defaultFontPixelHeight + spacing: ScreenTools.defaultFontPixelHeight * 0.25 property bool showSettingsIcon: _camera !== null property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle property var _dynamicCameras: _activeVehicle ? _activeVehicle.dynamicCameras : null property bool _isCamera: _dynamicCameras ? _dynamicCameras.cameras.count > 0 : false + property var _camera: _isCamera ? _dynamicCameras.cameras.get(0) : null // Single camera support for the time being property bool _cameraModeUndefined: _isCamera ? _dynamicCameras.cameras.get(0).cameraMode === QGCCameraControl.CAMERA_MODE_UNDEFINED : true property bool _cameraVideoMode: _isCamera ? _dynamicCameras.cameras.get(0).cameraMode === 1 : false property bool _cameraPhotoMode: _isCamera ? _dynamicCameras.cameras.get(0).cameraMode === 0 : false - property var _camera: _isCamera ? _dynamicCameras.cameras.get(0) : null // Single camera support for the time being + property bool _cameraPhotoIdle: _isCamera && _camera.photoStatus === QGCCameraControl.PHOTO_CAPTURE_IDLE + property bool _cameraElapsedMode: _isCamera && _camera.cameraMode === QGCCameraControl.CAM_MODE_PHOTO && _camera.photoMode === QGCCameraControl.PHOTO_CAPTURE_TIMELAPSE property real _spacers: ScreenTools.defaultFontPixelHeight * 0.5 property real _labelFieldWidth: ScreenTools.defaultFontPixelWidth * 30 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 property bool _communicationLost: _activeVehicle ? _activeVehicle.connectionLost : false property bool _hasModes: _isCamera && _camera && _camera.hasModes property bool _videoRecording: _camera && _camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING + property bool _noStorage: _camera && _camera.storageTotal === 0 function showSettings() { qgcView.showDialog(cameraSettings, _cameraVideoMode ? qsTr("Video Settings") : qsTr("Camera Settings"), 70, StandardButton.Ok) @@ -60,13 +63,21 @@ Column { //-- Actual controller QGCLabel { id: cameraLabel - text: _isCamera ? _dynamicCameras.cameras.get(0).modelName : qsTr("Camera") + text: _isCamera ? _camera.modelName : qsTr("Camera") visible: _isCamera font.pointSize: ScreenTools.smallFontPointSize anchors.horizontalCenter: parent.horizontalCenter } + QGCLabel { + text: _camera ? qsTr("Free Space: ") + _camera.storageFreeStr : "" + font.pointSize: ScreenTools.smallFontPointSize + anchors.horizontalCenter: parent.horizontalCenter + visible: _camera && !_noStorage + } //-- Camera Mode (visible only if camera has modes) + Item { width: 1; height: ScreenTools.defaultFontPixelHeight * 0.75; visible: camMode.visible; } Rectangle { + id: camMode width: _hasModes ? ScreenTools.defaultFontPixelWidth * 8 : 0 height: _hasModes ? ScreenTools.defaultFontPixelWidth * 4 : 0 color: qgcPal.button @@ -129,19 +140,21 @@ Column { } } //-- Shutter + Item { width: 1; height: ScreenTools.defaultFontPixelHeight * 0.75; visible: camShutter.visible; } Rectangle { + id: camShutter color: Qt.rgba(0,0,0,0) width: ScreenTools.defaultFontPixelWidth * 6 height: width radius: width * 0.5 - visible: _isCamera + visible: _isCamera border.color: qgcPal.buttonText border.width: 3 anchors.horizontalCenter: parent.horizontalCenter Rectangle { - width: parent.width * (_videoRecording ? 0.5 : 0.75) + width: parent.width * (_videoRecording || (_cameraPhotoMode && !_cameraPhotoIdle && _cameraElapsedMode) ? 0.5 : 0.75) height: width - radius: _videoRecording ? 0 : width * 0.5 + radius: _videoRecording || (_cameraPhotoMode && !_cameraPhotoIdle && _cameraElapsedMode) ? 0 : width * 0.5 color: _cameraModeUndefined ? qgcPal.colorGrey : qgcPal.colorRed anchors.centerIn: parent } @@ -152,11 +165,28 @@ Column { if(_cameraVideoMode) { _camera.toggleVideo() } else { - _camera.takePhoto() + if(_cameraPhotoMode && !_cameraPhotoIdle && _cameraElapsedMode) { + _camera.stopTakePhoto() + } else { + _camera.takePhoto() + } } } } } + Item { width: 1; height: ScreenTools.defaultFontPixelHeight * 0.75; visible: _isCamera; } + QGCLabel { + text: (_cameraVideoMode && _camera.videoStatus === QGCCameraControl.VIDEO_CAPTURE_STATUS_RUNNING) ? _camera.recordTimeStr : "00:00:00" + font.pointSize: ScreenTools.smallFontPointSize + visible: _cameraVideoMode + anchors.horizontalCenter: parent.horizontalCenter + } + QGCLabel { + text: _activeVehicle && _cameraPhotoMode ? ('00000' + _activeVehicle.cameraTriggerPoints.count).slice(-5) : "00000" + font.pointSize: ScreenTools.smallFontPointSize + visible: _cameraPhotoMode + anchors.horizontalCenter: parent.horizontalCenter + } Item { width: 1; height: ScreenTools.defaultFontPixelHeight; visible: _isCamera; } Component { id: cameraSettings @@ -232,6 +262,54 @@ Column { } } //------------------------------------------- + //-- Time Lapse + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _cameraPhotoMode + property var photoModes: [qsTr("Single"), qsTr("Time Lapse")] + QGCLabel { + text: qsTr("Photo Mode") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + QGCComboBox { + id: photoModeCombo + width: _editFieldWidth + model: parent.photoModes + currentIndex: _camera ? _camera.photoMode : 0 + onActivated: _camera.photoMode = index + } + } + //------------------------------------------- + //-- Time Lapse Interval + Row { + spacing: ScreenTools.defaultFontPixelWidth + anchors.horizontalCenter: parent.horizontalCenter + visible: _cameraPhotoMode && _camera.photoMode === QGCCameraControl.PHOTO_CAPTURE_TIMELAPSE + QGCLabel { + text: qsTr("Photo Interval (seconds)") + width: _labelFieldWidth + anchors.verticalCenter: parent.verticalCenter + } + Item { + height: photoModeCombo.height + width: _editFieldWidth + QGCSlider { + maximumValue: 60 + minimumValue: 1 + stepSize: 1 + value: _camera ? _camera.photoLapse : 5 + displayValue: true + updateValueWhileDragging: true + anchors.fill: parent + onValueChanged: { + _camera.photoLapse = value + } + } + } + } + //------------------------------------------- //-- Reset Camera Row { spacing: ScreenTools.defaultFontPixelWidth diff --git a/src/FlightMap/Widgets/CenterMapDropButton.qml b/src/FlightMap/Widgets/CenterMapDropButton.qml index e4f410ade5b19721bcb221bc1243a4e1e208a0de..ca48c00e398a164fdcb5b336b4a847267a1534e9 100644 --- a/src/FlightMap/Widgets/CenterMapDropButton.qml +++ b/src/FlightMap/Widgets/CenterMapDropButton.qml @@ -61,7 +61,7 @@ DropButton { /// Fits the visible region of the map to inclues all of the specified coordinates. If no coordinates /// are specified the map will center to fitHomePosition() function fitMapViewportToAllCoordinates(coordList) { - if (coordList.length == 0) { + if (coordList.length === 0) { map.center = fitHomePosition() return } @@ -115,16 +115,17 @@ DropButton { } function addFenceItemCoordsForFit(coordList) { + var i var homePosition = fitHomePosition() if (homePosition.isValid && geoFenceController.circleEnabled) { var azimuthList = [ 0, 180, 90, 270 ] - for (var i=0; i 2) { - for (var i=0; i 2) { - for (var i=0; i<_geoFenceController.mapPolygon.path.count; i++) { + for (i = 0; i < _geoFenceController.mapPolygon.path.count; i++) { coordList.push(_geoFenceController.mapPolygon.path[i]) } } diff --git a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml index 4e2fe1dc7d3711e3b52a5ee00e4c89cd221691e4..290fec81fec381820564febe485d2dfd4644c683 100644 --- a/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml +++ b/src/FlightMap/Widgets/QGCInstrumentWidgetAlternate.qml @@ -20,7 +20,7 @@ import QGroundControl.Palette 1.0 /// Instrument panel shown when virtual thumbsticks are visible Rectangle { id: root - width: ScreenTools.isTinyScreen ? getPreferredInstrumentWidth() * 1.5 : getPreferredInstrumentWidth() + width: getPreferredInstrumentWidth() height: _outerRadius * 2 radius: _outerRadius color: qgcPal.window diff --git a/src/FlightMap/Widgets/ValuePageWidget.qml b/src/FlightMap/Widgets/ValuePageWidget.qml index 6d87e121dc01b047d1b04d6cf6855e16d9d96bd6..b68146cddc25713a12f2b5ac78d3dc5a79987aa1 100644 --- a/src/FlightMap/Widgets/ValuePageWidget.qml +++ b/src/FlightMap/Widgets/ValuePageWidget.qml @@ -82,7 +82,7 @@ Column { QGCLabel { width: parent.width horizontalAlignment: Text.AlignHCenter - fontSizeMode: Text.HorizontalFit + wrapMode: Text.WordWrap text: fact.shortDescription + (fact.units ? " (" + fact.units + ")" : "") } QGCLabel { @@ -105,9 +105,9 @@ Column { QGCLabel { width: parent.width + wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter font.pointSize: ScreenTools.isTinyScreen ? ScreenTools.smallFontPointSize * 0.75 : ScreenTools.smallFontPointSize - fontSizeMode: Text.HorizontalFit text: fact.shortDescription } QGCLabel { @@ -144,6 +144,8 @@ Column { anchors.right: parent.right spacing: _margins + /* + Leaving this here for now just in case FactCheckBox { text: qsTr("Show large compass") fact: _showLargeCompass @@ -151,6 +153,7 @@ Column { property Fact _showLargeCompass: QGroundControl.settingsManager.appSettings.showLargeCompass } + */ Item { width: 1 @@ -171,8 +174,8 @@ Column { anchors.right: parent.right sourceComponent: factGroupList - property var factGroup: _activeVehicle - property var factGroupName: "Vehicle" + property var factGroup: _activeVehicle + property string factGroupName: "Vehicle" } } } diff --git a/src/FlightMap/Widgets/VideoPageWidget.qml b/src/FlightMap/Widgets/VideoPageWidget.qml new file mode 100644 index 0000000000000000000000000000000000000000..10d0e6503e9577ee7784a0fcef6bae1efc2a5258 --- /dev/null +++ b/src/FlightMap/Widgets/VideoPageWidget.qml @@ -0,0 +1,150 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.4 +import QtPositioning 5.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 1.4 +import QtQuick.Dialogs 1.2 +import QtGraphicalEffects 1.0 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 + +/// Video streaming page for Instrument Panel PageView +Item { + width: pageWidth + height: videoGrid.height + (ScreenTools.defaultFontPixelHeight * 2) + anchors.margins: ScreenTools.defaultFontPixelWidth * 2 + anchors.centerIn: parent + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _communicationLost: _activeVehicle ? _activeVehicle.connectionLost : false + property var _videoReceiver: QGroundControl.videoManager.videoReceiver + property bool _recordingVideo: _videoReceiver && _videoReceiver.recording + property bool _videoRunning: _videoReceiver && _videoReceiver.videoRunning + property bool _streamingEnabled: QGroundControl.settingsManager.videoSettings.streamConfigured + + QGCPalette { id:qgcPal; colorGroupEnabled: true } + + GridLayout { + id: videoGrid + columns: 2 + columnSpacing: ScreenTools.defaultFontPixelWidth * 2 + rowSpacing: ScreenTools.defaultFontPixelHeight + anchors.centerIn: parent + Connections { + // For some reason, the normal signal is not reflected in the control below + target: QGroundControl.settingsManager.videoSettings.streamEnabled + onRawValueChanged: { + enableSwitch.checked = QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue + } + } + // Enable/Disable Video Streaming + QGCLabel { + text: qsTr("Enable Stream") + font.pointSize: ScreenTools.smallFontPointSize + } + QGCSwitch { + id: enableSwitch + enabled: _streamingEnabled + checked: QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue + onClicked: { + if(checked) { + QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 1 + _videoReceiver.start() + } else { + QGroundControl.settingsManager.videoSettings.streamEnabled.rawValue = 0 + _videoReceiver.stop() + } + } + } + // Grid Lines + QGCLabel { + text: qsTr("Grid Lines") + font.pointSize: ScreenTools.smallFontPointSize + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible + } + QGCSwitch { + enabled: _streamingEnabled && _activeVehicle + checked: QGroundControl.settingsManager.videoSettings.gridLines.rawValue + visible: QGroundControl.videoManager.isGStreamer && QGroundControl.settingsManager.videoSettings.gridLines.visible + onClicked: { + if(checked) { + QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 1 + } else { + QGroundControl.settingsManager.videoSettings.gridLines.rawValue = 0 + } + } + } + QGCLabel { + text: _recordingVideo ? qsTr("Stop Recording") : qsTr("Record Stream") + font.pointSize: ScreenTools.smallFontPointSize + visible: QGroundControl.settingsManager.videoSettings.showRecControl.rawValue + } + // Button to start/stop video recording + Item { + anchors.margins: ScreenTools.defaultFontPixelHeight / 2 + height: ScreenTools.defaultFontPixelHeight * 2 + width: height + Layout.alignment: Qt.AlignHCenter + visible: QGroundControl.settingsManager.videoSettings.showRecControl.rawValue + Rectangle { + id: recordBtnBackground + anchors.top: parent.top + anchors.bottom: parent.bottom + width: height + radius: _recordingVideo ? 0 : height + color: (_videoRunning && _streamingEnabled) ? "red" : "gray" + SequentialAnimation on opacity { + running: _recordingVideo + loops: Animation.Infinite + PropertyAnimation { to: 0.5; duration: 500 } + PropertyAnimation { to: 1.0; duration: 500 } + } + } + QGCColoredImage { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + width: height * 0.625 + sourceSize.width: width + source: "/qmlimages/CameraIcon.svg" + visible: recordBtnBackground.visible + fillMode: Image.PreserveAspectFit + color: "white" + } + MouseArea { + anchors.fill: parent + enabled: _videoRunning && _streamingEnabled + onClicked: { + if (_recordingVideo) { + _videoReceiver.stopRecording() + // reset blinking animation + recordBtnBackground.opacity = 1 + } else { + _videoReceiver.startRecording() + } + } + } + } + QGCLabel { + text: qsTr("Video Streaming Not Configured") + font.pointSize: ScreenTools.smallFontPointSize + visible: !_streamingEnabled + Layout.columnSpan: 2 + } + } +} diff --git a/src/GPS/RTCM/RTCMMavlink.cc b/src/GPS/RTCM/RTCMMavlink.cc index d8f6c92df4ad2dc5d15e96edba5fac513f753151..e81dd2bff3cf98bebce3a16ccc31915a7e8b5a54 100644 --- a/src/GPS/RTCM/RTCMMavlink.cc +++ b/src/GPS/RTCM/RTCMMavlink.cc @@ -38,29 +38,26 @@ void RTCMMavlink::RTCMDataUpdate(QByteArray message) if (message.size() < maxMessageLength) { mavlinkRtcmData.len = message.size(); + mavlinkRtcmData.flags = (_sequenceId & 0x1F) << 3; memcpy(&mavlinkRtcmData.data, message.data(), message.size()); sendMessageToVehicle(mavlinkRtcmData); } else { // We need to fragment - static uint8_t sequenceId = 0; // Sequence id is used to indicate that the individual fragements belong to the same set uint8_t fragmentId = 0; // Fragment id indicates the fragment within a set - int start = 0; while (start < message.size()) { int length = std::min(message.size() - start, maxMessageLength); mavlinkRtcmData.flags = 1; // LSB set indicates message is fragmented mavlinkRtcmData.flags |= fragmentId++ << 1; // Next 2 bits are fragment id - mavlinkRtcmData.flags |= sequenceId++ << 3; // Next 5 bits are sequence id + mavlinkRtcmData.flags |= (_sequenceId & 0x1F) << 3; // Next 5 bits are sequence id mavlinkRtcmData.len = length; memcpy(&mavlinkRtcmData.data, message.data() + start, length); sendMessageToVehicle(mavlinkRtcmData); start += length; } - if (sequenceId == 0x1F) { - sequenceId = 0; - } } + ++_sequenceId; } void RTCMMavlink::sendMessageToVehicle(const mavlink_gps_rtcm_data_t& msg) diff --git a/src/GPS/RTCM/RTCMMavlink.h b/src/GPS/RTCM/RTCMMavlink.h index 6c59974e38809e2f72f0b6c9d97d9946ff01cd89..a568a2d36b82c374b99113b87482da7996ea95d7 100644 --- a/src/GPS/RTCM/RTCMMavlink.h +++ b/src/GPS/RTCM/RTCMMavlink.h @@ -36,4 +36,5 @@ private: QGCToolbox& _toolbox; QElapsedTimer _bandwidthTimer; int _bandwidthByteCounter = 0; + uint8_t _sequenceId = 0; }; diff --git a/src/Joystick/Joystick.cc b/src/Joystick/Joystick.cc index a7d4f734fb31216c6fd64ab0ca8b2c8e1c066166..3f0ba6aeb22ea2bead37025410237e5f6b698a4f 100644 --- a/src/Joystick/Joystick.cc +++ b/src/Joystick/Joystick.cc @@ -32,6 +32,11 @@ const char* Joystick::_roverTXModeSettingsKey = "TXMode_Rover"; const char* Joystick::_vtolTXModeSettingsKey = "TXMode_VTOL"; const char* Joystick::_submarineTXModeSettingsKey = "TXMode_Submarine"; +const char* Joystick::_buttonActionArm = QT_TR_NOOP("Arm"); +const char* Joystick::_buttonActionDisarm = QT_TR_NOOP("Disarm"); +const char* Joystick::_buttonActionVTOLFixedWing = QT_TR_NOOP("VTOL: Fixed Wing"); +const char* Joystick::_buttonActionVTOLMultiRotor = QT_TR_NOOP("VTOL: Multi-Rotor"); + const char* Joystick::_rgFunctionSettingsKey[Joystick::maxFunction] = { "RollAxis", "PitchAxis", @@ -49,7 +54,7 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC , _hatCount(hatCount) , _hatButtonCount(4*hatCount) , _totalButtonCount(_buttonCount+_hatButtonCount) - , _calibrationMode(CalibrationModeOff) + , _calibrationMode(false) , _rgAxisValues(NULL) , _rgCalibration(NULL) , _rgButtonValues(NULL) @@ -84,6 +89,10 @@ Joystick::Joystick(const QString& name, int axisCount, int buttonCount, int hatC Joystick::~Joystick() { + // Crash out of the thread if it is still running + terminate(); + wait(); + delete[] _rgAxisValues; delete[] _rgCalibration; delete[] _rgButtonValues; @@ -431,7 +440,7 @@ void Joystick::run(void) } } - if (_calibrationMode != CalibrationModeCalibrating && _calibrated) { + if (_outputEnabled && _calibrated) { int axis = _rgFunctionAxis[rollFunction]; float roll = _adjustRange(_rgAxisValues[axis], _rgCalibration[axis], _deadband); @@ -638,11 +647,12 @@ QStringList Joystick::actions(void) { QStringList list; - list << "Arm" << "Disarm"; + list << _buttonActionArm << _buttonActionDisarm; if (_activeVehicle) { list << _activeVehicle->flightModes(); } + list << _buttonActionVTOLFixedWing << _buttonActionVTOLMultiRotor; return list; } @@ -757,48 +767,44 @@ void Joystick::setDeadband(bool deadband) _saveSettings(); } -void Joystick::startCalibrationMode(CalibrationMode_t mode) +void Joystick::setCalibrationMode(bool calibrating) { - if (mode == CalibrationModeOff) { - qWarning() << "Incorrect mode CalibrationModeOff"; - return; - } + _calibrationMode = calibrating; - _calibrationMode = mode; - - if (!isRunning()) { + if (calibrating && !isRunning()) { _pollingStartedForCalibration = true; startPolling(_multiVehicleManager->activeVehicle()); } -} - -void Joystick::stopCalibrationMode(CalibrationMode_t mode) -{ - if (mode == CalibrationModeOff) { - qWarning() << "Incorrect mode: CalibrationModeOff"; - return; + else if (_pollingStartedForCalibration) { + stopPolling(); } - - if (mode == CalibrationModeCalibrating) { - _calibrationMode = CalibrationModeMonitor; - } else { - _calibrationMode = CalibrationModeOff; - if (_pollingStartedForCalibration) { - stopPolling(); - } + if (calibrating){ + setOutputEnabled(false); //Disable the joystick output before calibrating + } + else if (!calibrating && _calibrated){ + setOutputEnabled(true); //Enable joystick output after calibration } } +void Joystick::setOutputEnabled(bool enabled){ + _outputEnabled = enabled; + emit outputEnabledChanged(_outputEnabled); +} + void Joystick::_buttonAction(const QString& action) { if (!_activeVehicle || !_activeVehicle->joystickEnabled()) { return; } - if (action == "Arm") { + if (action == _buttonActionArm) { _activeVehicle->setArmed(true); - } else if (action == "Disarm") { + } else if (action == _buttonActionDisarm) { _activeVehicle->setArmed(false); + } else if (action == _buttonActionVTOLFixedWing) { + _activeVehicle->setVtolInFwdFlight(true); + } else if (action == _buttonActionVTOLMultiRotor) { + _activeVehicle->setVtolInFwdFlight(false); } else if (_activeVehicle->flightModes().contains(action)) { _activeVehicle->setFlightMode(action); } else { diff --git a/src/Joystick/Joystick.h b/src/Joystick/Joystick.h index d6f15ed8bf7fd300c9eba04c23f238125a481544..8cd6accf66306b3b2e89ec6d0512bdc06f3e6234 100644 --- a/src/Joystick/Joystick.h +++ b/src/Joystick/Joystick.h @@ -61,6 +61,7 @@ public: Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(bool calibrated MEMBER _calibrated NOTIFY calibratedChanged) + Q_PROPERTY(bool outputEnabled MEMBER _outputEnabled WRITE setOutputEnabled NOTIFY outputEnabledChanged) Q_PROPERTY(int totalButtonCount READ totalButtonCount CONSTANT) Q_PROPERTY(int axisCount READ axisCount CONSTANT) @@ -122,20 +123,13 @@ public: void setTXMode(int mode); int getTXMode(void) { return _transmitterMode; } - typedef enum { - CalibrationModeOff, // Not calibrating - CalibrationModeMonitor, // Monitors are active, continue to send to vehicle if already polling - CalibrationModeCalibrating, // Calibrating, stop sending joystick to vehicle - } CalibrationMode_t; - /// Set the current calibration mode - void startCalibrationMode(CalibrationMode_t mode); - - /// Clear the current calibration mode - void stopCalibrationMode(CalibrationMode_t mode); + void setCalibrationMode(bool calibrating); + void setOutputEnabled(bool enabled); signals: void calibratedChanged(bool calibrated); + void outputEnabledChanged(bool enabled); // The raw signals are only meant for use by calibration void rawAxisValueChanged(int index, int value); @@ -201,7 +195,8 @@ protected: int _totalButtonCount; static int _transmitterMode; - CalibrationMode_t _calibrationMode; + bool _calibrationMode; + bool _outputEnabled; int* _rgAxisValues; Calibration_t* _rgCalibration; @@ -241,6 +236,11 @@ private: static const char* _vtolTXModeSettingsKey; static const char* _submarineTXModeSettingsKey; + static const char* _buttonActionArm; + static const char* _buttonActionDisarm; + static const char* _buttonActionVTOLFixedWing; + static const char* _buttonActionVTOLMultiRotor; + private slots: void _activeVehicleChanged(Vehicle* activeVehicle); }; diff --git a/src/MissionManager/CameraCalc.cc b/src/MissionManager/CameraCalc.cc index 7e0d651570dab479d2db816123be6c460e7af034..a1efa9c5e71c18a8cf119f9e5b9e960bfedb3a5f 100644 --- a/src/MissionManager/CameraCalc.cc +++ b/src/MissionManager/CameraCalc.cc @@ -14,22 +14,24 @@ #include -const char* CameraCalc::_valueSetIsDistanceName = "ValueSetIsDistance"; -const char* CameraCalc::_distanceToSurfaceName = "DistanceToSurface"; -const char* CameraCalc::_imageDensityName = "ImageDensity"; -const char* CameraCalc::_frontalOverlapName = "FrontalOverlap"; -const char* CameraCalc::_sideOverlapName = "SideOverlap"; -const char* CameraCalc::_adjustedFootprintFrontalName = "AdjustedFootprintFrontal"; -const char* CameraCalc::_adjustedFootprintSideName = "AdjustedFootprintSide"; -const char* CameraCalc::_jsonCameraSpecTypeKey = "CameraSpecType"; -const char* CameraCalc::_jsonKnownCameraNameKey = "CameraName"; +const char* CameraCalc::_valueSetIsDistanceName = "ValueSetIsDistance"; +const char* CameraCalc::_distanceToSurfaceName = "DistanceToSurface"; +const char* CameraCalc::_distanceToSurfaceRelativeName = "DistanceToSurfaceRelative"; +const char* CameraCalc::_imageDensityName = "ImageDensity"; +const char* CameraCalc::_frontalOverlapName = "FrontalOverlap"; +const char* CameraCalc::_sideOverlapName = "SideOverlap"; +const char* CameraCalc::_adjustedFootprintFrontalName = "AdjustedFootprintFrontal"; +const char* CameraCalc::_adjustedFootprintSideName = "AdjustedFootprintSide"; +const char* CameraCalc::_jsonCameraNameKey = "CameraName"; +const char* CameraCalc::_jsonCameraSpecTypeKey = "CameraSpecType"; CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent) : CameraSpec (parent) , _vehicle (vehicle) , _dirty (false) - , _cameraSpecType (CameraSpecNone) + , _cameraName (manualCameraName()) , _disableRecalc (false) + , _distanceToSurfaceRelative (true) , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CameraCalc.FactMetaData.json"), this)) , _valueSetIsDistanceFact (0, _valueSetIsDistanceName, FactMetaData::valueTypeBool) , _distanceToSurfaceFact (0, _distanceToSurfaceName, FactMetaData::valueTypeDouble) @@ -52,9 +54,18 @@ CameraCalc::CameraCalc(Vehicle* vehicle, QObject* parent) _adjustedFootprintSideFact.setMetaData (_metaDataMap[_adjustedFootprintSideName], true); _adjustedFootprintFrontalFact.setMetaData (_metaDataMap[_adjustedFootprintFrontalName], true); - connect(this, &CameraCalc::knownCameraNameChanged, this, &CameraCalc::_knownCameraNameChanged); + connect(&_valueSetIsDistanceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_distanceToSurfaceFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_imageDensityFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_frontalOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_sideOverlapFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_adjustedFootprintSideFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(&_adjustedFootprintFrontalFact, &Fact::valueChanged, this, &CameraCalc::_setDirty); + connect(this, &CameraCalc::cameraNameChanged, this, &CameraCalc::_setDirty); + connect(this, &CameraCalc::distanceToSurfaceRelativeChanged, this, &CameraCalc::_setDirty); - connect(this, &CameraCalc::cameraSpecTypeChanged, this, &CameraCalc::_recalcTriggerDistance); + connect(this, &CameraCalc::cameraNameChanged, this, &CameraCalc::_recalcTriggerDistance); + connect(this, &CameraCalc::cameraNameChanged, this, &CameraCalc::_adjustDistanceToSurfaceRelative); connect(&_distanceToSurfaceFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance); connect(&_imageDensityFact, &Fact::rawValueChanged, this, &CameraCalc::_recalcTriggerDistance); @@ -73,59 +84,57 @@ void CameraCalc::setDirty(bool dirty) } } -void CameraCalc::setCameraSpecType(CameraSpecType cameraSpecType) +void CameraCalc::setCameraName(QString cameraName) { - if (cameraSpecType != _cameraSpecType) { - _cameraSpecType = cameraSpecType; - emit cameraSpecTypeChanged(_cameraSpecType); - } -} - -void CameraCalc::setKnownCameraName(QString knownCameraName) -{ - if (knownCameraName != _knownCameraName) { - _knownCameraName = knownCameraName; - emit knownCameraNameChanged(_knownCameraName); - } -} - -void CameraCalc::_knownCameraNameChanged(QString knownCameraName) -{ - if (_cameraSpecType == CameraSpecKnown) { - CameraMetaData* cameraMetaData = NULL; - - // Update camera specs to new camera - for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) { - cameraMetaData = _knownCameraList[cameraIndex].value(); - if (knownCameraName == cameraMetaData->name()) { - break; + if (cameraName != _cameraName) { + _cameraName = cameraName; + + if (_cameraName == manualCameraName() || _cameraName == customCameraName()) { + // These values are unknown for these types + fixedOrientation()->setRawValue(false); + minTriggerInterval()->setRawValue(0); + if (_cameraName == manualCameraName()) { + valueSetIsDistance()->setRawValue(false); } - } - - _disableRecalc = true; - if (cameraMetaData) { - sensorWidth()->setRawValue (cameraMetaData->sensorWidth()); - sensorHeight()->setRawValue (cameraMetaData->sensorHeight()); - imageWidth()->setRawValue (cameraMetaData->imageWidth()); - imageHeight()->setRawValue (cameraMetaData->imageHeight()); - focalLength()->setRawValue (cameraMetaData->focalLength()); - landscape()->setRawValue (cameraMetaData->landscape()); - fixedOrientation()->setRawValue (cameraMetaData->fixedOrientation()); - minTriggerInterval()->setRawValue (cameraMetaData->minTriggerInterval()); } else { - // We don't know this camera, switch back to custom - _cameraSpecType = CameraSpecCustom; - emit cameraSpecTypeChanged(_cameraSpecType); + // This should be a known camera name. Update camera specs to new camera + + bool foundKnownCamera = false; + CameraMetaData* cameraMetaData = NULL; + for (int cameraIndex=0; cameraIndex<_knownCameraList.count(); cameraIndex++) { + cameraMetaData = _knownCameraList[cameraIndex].value(); + if (cameraName == cameraMetaData->name()) { + foundKnownCamera = true; + break; + } + } + + _disableRecalc = true; + if (foundKnownCamera) { + sensorWidth()->setRawValue (cameraMetaData->sensorWidth()); + sensorHeight()->setRawValue (cameraMetaData->sensorHeight()); + imageWidth()->setRawValue (cameraMetaData->imageWidth()); + imageHeight()->setRawValue (cameraMetaData->imageHeight()); + focalLength()->setRawValue (cameraMetaData->focalLength()); + landscape()->setRawValue (cameraMetaData->landscape()); + fixedOrientation()->setRawValue (cameraMetaData->fixedOrientation()); + minTriggerInterval()->setRawValue (cameraMetaData->minTriggerInterval()); + } else { + // We don't know this camera, switch back to custom + _cameraName = customCameraName(); + fixedOrientation()->setRawValue(false); + minTriggerInterval()->setRawValue(0); + } + _disableRecalc = false; } - _disableRecalc = false; - _recalcTriggerDistance(); + emit cameraNameChanged(_cameraName); } } void CameraCalc::_recalcTriggerDistance(void) { - if (_disableRecalc || _cameraSpecType == CameraSpecNone) { + if (_disableRecalc || _cameraName == manualCameraName()) { return; } @@ -168,14 +177,15 @@ void CameraCalc::_recalcTriggerDistance(void) void CameraCalc::save(QJsonObject& json) const { - json[_jsonCameraSpecTypeKey] = (int)_cameraSpecType; + json[JsonHelper::jsonVersionKey] = 1; json[_adjustedFootprintSideName] = _adjustedFootprintSideFact.rawValue().toDouble(); json[_adjustedFootprintFrontalName] = _adjustedFootprintFrontalFact.rawValue().toDouble(); - json[_distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble(); + json[_distanceToSurfaceName] = _distanceToSurfaceFact.rawValue().toDouble(); + json[_distanceToSurfaceRelativeName] = _distanceToSurfaceRelative; + json[_jsonCameraNameKey] = _cameraName; - if (_cameraSpecType != CameraSpecNone) { + if (_cameraName != manualCameraName()) { CameraSpec::save(json); - json[_jsonKnownCameraNameKey] = _knownCameraName; json[_valueSetIsDistanceName] = _valueSetIsDistanceFact.rawValue().toBool(); json[_imageDensityName] = _imageDensityFact.rawValue().toDouble(); json[_frontalOverlapName] = _frontalOverlapFact.rawValue().toDouble(); @@ -184,55 +194,68 @@ void CameraCalc::save(QJsonObject& json) const } bool CameraCalc::load(const QJsonObject& json, QString& errorString) -{ +{ + QJsonObject v1Json = json; + + if (!v1Json.contains(JsonHelper::jsonVersionKey)) { + // Version 0 file. Differences from Version 1 for conversion: + // JsonHelper::jsonVersionKey not stored + // _jsonCameraSpecTypeKey stores CameraSpecType + // _jsonCameraNameKey only set if CameraSpecKnown + int cameraSpec = v1Json[_jsonCameraSpecTypeKey].toInt(CameraSpecNone); + if (cameraSpec == CameraSpecCustom) { + v1Json[_jsonCameraNameKey] = customCameraName(); + } else if (cameraSpec == CameraSpecNone) { + v1Json[_jsonCameraNameKey] = manualCameraName(); + } + v1Json.remove(_jsonCameraSpecTypeKey); + v1Json[JsonHelper::jsonVersionKey] = 1; + } + + int version = v1Json[JsonHelper::jsonVersionKey].toInt(); + if (version != 1) { + errorString = tr("CameraCalc section version %1 not supported").arg(version); + return false; + } + QList keyInfoList1 = { - { _jsonCameraSpecTypeKey, QJsonValue::Double, true }, + { _jsonCameraNameKey, QJsonValue::String, true }, { _adjustedFootprintSideName, QJsonValue::Double, true }, { _adjustedFootprintFrontalName, QJsonValue::Double, true }, { _distanceToSurfaceName, QJsonValue::Double, true }, + { _distanceToSurfaceRelativeName, QJsonValue::Bool, true }, }; - if (!JsonHelper::validateKeys(json, keyInfoList1, errorString)) { - return false; - } - - int cameraSpecType = json[_jsonCameraSpecTypeKey].toInt(); - switch (cameraSpecType) { - case CameraSpecNone: - case CameraSpecCustom: - case CameraSpecKnown: - break; - default: - errorString = tr("Unsupported CameraSpecType %d").arg(cameraSpecType); + if (!JsonHelper::validateKeys(v1Json, keyInfoList1, errorString)) { return false; } _disableRecalc = true; - setCameraSpecType((CameraSpecType)cameraSpecType); - _adjustedFootprintSideFact.setRawValue (json[_adjustedFootprintSideName].toDouble()); - _adjustedFootprintFrontalFact.setRawValue (json[_adjustedFootprintFrontalName].toDouble()); - _distanceToSurfaceFact.setRawValue (json[_distanceToSurfaceName].toDouble()); + setCameraName(v1Json[_jsonCameraNameKey].toString()); + _adjustedFootprintSideFact.setRawValue (v1Json[_adjustedFootprintSideName].toDouble()); + _adjustedFootprintFrontalFact.setRawValue (v1Json[_adjustedFootprintFrontalName].toDouble()); + _distanceToSurfaceFact.setRawValue (v1Json[_distanceToSurfaceName].toDouble()); - if (_cameraSpecType != CameraSpecNone) { + _distanceToSurfaceRelative = v1Json[_distanceToSurfaceRelativeName].toBool(); + + if (_cameraName != manualCameraName()) { QList keyInfoList2 = { - { _jsonKnownCameraNameKey, QJsonValue::String, true }, { _valueSetIsDistanceName, QJsonValue::Bool, true }, { _imageDensityName, QJsonValue::Double, true }, { _frontalOverlapName, QJsonValue::Double, true }, { _sideOverlapName, QJsonValue::Double, true }, }; - if (!JsonHelper::validateKeys(json, keyInfoList2, errorString)) { + if (!JsonHelper::validateKeys(v1Json, keyInfoList2, errorString)) { return false; _disableRecalc = false; } - setKnownCameraName(json[_jsonKnownCameraNameKey].toString()); - _valueSetIsDistanceFact.setRawValue (json[_valueSetIsDistanceName].toBool()); - _imageDensityFact.setRawValue (json[_imageDensityName].toDouble()); - _frontalOverlapFact.setRawValue (json[_frontalOverlapName].toDouble()); - _sideOverlapFact.setRawValue (json[_sideOverlapName].toDouble()); + _valueSetIsDistanceFact.setRawValue (v1Json[_valueSetIsDistanceName].toBool()); + _imageDensityFact.setRawValue (v1Json[_imageDensityName].toDouble()); + _frontalOverlapFact.setRawValue (v1Json[_frontalOverlapName].toDouble()); + _sideOverlapFact.setRawValue (v1Json[_sideOverlapName].toDouble()); - if (!CameraSpec::load(json, errorString)) { + if (!CameraSpec::load(v1Json, errorString)) { return false; } } @@ -241,3 +264,33 @@ bool CameraCalc::load(const QJsonObject& json, QString& errorString) return true; } + +QString CameraCalc::customCameraName(void) +{ + return tr("Custom Camera"); +} + +QString CameraCalc::manualCameraName(void) +{ + return tr("Manual (no camera specs)"); +} + +void CameraCalc::_adjustDistanceToSurfaceRelative(void) +{ + if (!isManualCamera()) { + setDistanceToSurfaceRelative(true); + } +} + +void CameraCalc::setDistanceToSurfaceRelative(bool distanceToSurfaceRelative) +{ + if (distanceToSurfaceRelative != _distanceToSurfaceRelative) { + _distanceToSurfaceRelative = distanceToSurfaceRelative; + emit distanceToSurfaceRelativeChanged(distanceToSurfaceRelative); + } +} + +void CameraCalc::_setDirty(void) +{ + setDirty(true); +} diff --git a/src/MissionManager/CameraCalc.h b/src/MissionManager/CameraCalc.h index be714a1eb1c20ecdaf713a3ea39a22d052ca5670..953114ec5b6cb3950404350a16660311e8605873 100644 --- a/src/MissionManager/CameraCalc.h +++ b/src/MissionManager/CameraCalc.h @@ -20,32 +20,27 @@ class CameraCalc : public CameraSpec public: CameraCalc(Vehicle* vehicle, QObject* parent = NULL); - Q_ENUMS(CameraSpecType) - - Q_PROPERTY(CameraSpecType cameraSpecType READ cameraSpecType WRITE setCameraSpecType NOTIFY cameraSpecTypeChanged) - Q_PROPERTY(QString knownCameraName READ knownCameraName WRITE setKnownCameraName NOTIFY knownCameraNameChanged) - Q_PROPERTY(Fact* valueSetIsDistance READ valueSetIsDistance CONSTANT) ///< true: distance specified, resolution calculated - Q_PROPERTY(Fact* distanceToSurface READ distanceToSurface CONSTANT) ///< Distance to surface for image foot print calculation - Q_PROPERTY(Fact* imageDensity READ imageDensity CONSTANT) ///< Image density on surface (cm/px) - Q_PROPERTY(Fact* frontalOverlap READ frontalOverlap CONSTANT) - Q_PROPERTY(Fact* sideOverlap READ sideOverlap CONSTANT) - Q_PROPERTY(Fact* adjustedFootprintSide READ adjustedFootprintSide CONSTANT) ///< Side footprint adjusted down for overlap - Q_PROPERTY(Fact* adjustedFootprintFrontal READ adjustedFootprintFrontal CONSTANT) ///< Frontal footprint adjusted down for overlap + Q_PROPERTY(QString cameraName READ cameraName WRITE setCameraName NOTIFY cameraNameChanged) + Q_PROPERTY(QString customCameraName READ customCameraName CONSTANT) ///< Camera name for custom camera setting + Q_PROPERTY(QString manualCameraName READ manualCameraName CONSTANT) ///< Camera name for manual camera setting + Q_PROPERTY(bool isManualCamera READ isManualCamera NOTIFY cameraNameChanged) ///< true: using manual camera + Q_PROPERTY(Fact* valueSetIsDistance READ valueSetIsDistance CONSTANT) ///< true: distance specified, resolution calculated + Q_PROPERTY(Fact* distanceToSurface READ distanceToSurface CONSTANT) ///< Distance to surface for image foot print calculation + Q_PROPERTY(Fact* imageDensity READ imageDensity CONSTANT) ///< Image density on surface (cm/px) + Q_PROPERTY(Fact* frontalOverlap READ frontalOverlap CONSTANT) + Q_PROPERTY(Fact* sideOverlap READ sideOverlap CONSTANT) + Q_PROPERTY(Fact* adjustedFootprintSide READ adjustedFootprintSide CONSTANT) ///< Side footprint adjusted down for overlap + Q_PROPERTY(Fact* adjustedFootprintFrontal READ adjustedFootprintFrontal CONSTANT) ///< Frontal footprint adjusted down for overlap + Q_PROPERTY(bool distanceToSurfaceRelative READ distanceToSurfaceRelative WRITE setDistanceToSurfaceRelative NOTIFY distanceToSurfaceRelativeChanged) // The following values are calculated from the camera properties - Q_PROPERTY(double imageFootprintSide READ imageFootprintSide NOTIFY imageFootprintSideChanged) ///< Size of image size side in meters - Q_PROPERTY(double imageFootprintFrontal READ imageFootprintFrontal NOTIFY imageFootprintFrontalChanged) ///< Size of image size frontal in meters + Q_PROPERTY(double imageFootprintSide READ imageFootprintSide NOTIFY imageFootprintSideChanged) ///< Size of image size side in meters + Q_PROPERTY(double imageFootprintFrontal READ imageFootprintFrontal NOTIFY imageFootprintFrontalChanged) ///< Size of image size frontal in meters - enum CameraSpecType { - CameraSpecNone, - CameraSpecCustom, - CameraSpecKnown - }; - - CameraSpecType cameraSpecType(void) const { return _cameraSpecType; } - QString knownCameraName(void) const { return _knownCameraName; } - void setCameraSpecType(CameraSpecType cameraSpecType); - void setKnownCameraName(QString knownCameraName); + static QString customCameraName(void); + static QString manualCameraName(void); + QString cameraName(void) const { return _cameraName; } + void setCameraName(QString cameraName); Fact* valueSetIsDistance (void) { return &_valueSetIsDistanceFact; } Fact* distanceToSurface (void) { return &_distanceToSurfaceFact; } @@ -55,32 +50,44 @@ public: Fact* adjustedFootprintSide (void) { return &_adjustedFootprintSideFact; } Fact* adjustedFootprintFrontal (void) { return &_adjustedFootprintFrontalFact; } - double imageFootprintSide (void) const { return _imageFootprintSide; } - double imageFootprintFrontal (void) const { return _imageFootprintFrontal; } + const Fact* valueSetIsDistance (void) const { return &_valueSetIsDistanceFact; } + const Fact* distanceToSurface (void) const { return &_distanceToSurfaceFact; } + const Fact* imageDensity (void) const { return &_imageDensityFact; } + const Fact* frontalOverlap (void) const { return &_frontalOverlapFact; } + const Fact* sideOverlap (void) const { return &_sideOverlapFact; } + const Fact* adjustedFootprintSide (void) const { return &_adjustedFootprintSideFact; } + const Fact* adjustedFootprintFrontal (void) const { return &_adjustedFootprintFrontalFact; } - bool dirty (void) const { return _dirty; } - void setDirty (bool dirty); + bool dirty (void) const { return _dirty; } + bool isManualCamera (void) { return cameraName() == manualCameraName(); } + double imageFootprintSide (void) const { return _imageFootprintSide; } + double imageFootprintFrontal (void) const { return _imageFootprintFrontal; } + bool distanceToSurfaceRelative (void) const { return _distanceToSurfaceRelative; } + + void setDirty (bool dirty); + void setDistanceToSurfaceRelative (bool distanceToSurfaceRelative); void save(QJsonObject& json) const; bool load(const QJsonObject& json, QString& errorString); signals: - void cameraSpecTypeChanged (CameraSpecType cameraSpecType); - void knownCameraNameChanged (QString knownCameraName); - void dirtyChanged (bool dirty); - void imageFootprintSideChanged (double imageFootprintSide); - void imageFootprintFrontalChanged (double imageFootprintFrontal); + void cameraNameChanged (QString cameraName); + void dirtyChanged (bool dirty); + void imageFootprintSideChanged (double imageFootprintSide); + void imageFootprintFrontalChanged (double imageFootprintFrontal); + void distanceToSurfaceRelativeChanged (bool distanceToSurfaceRelative); private slots: - void _knownCameraNameChanged(QString knownCameraName); - void _recalcTriggerDistance(void); + void _recalcTriggerDistance (void); + void _adjustDistanceToSurfaceRelative (void); + void _setDirty (void); private: Vehicle* _vehicle; bool _dirty; - CameraSpecType _cameraSpecType; - QString _knownCameraName; + QString _cameraName; bool _disableRecalc; + bool _distanceToSurfaceRelative; QMap _metaDataMap; @@ -99,11 +106,21 @@ private: static const char* _valueSetIsDistanceName; static const char* _distanceToSurfaceName; + static const char* _distanceToSurfaceRelativeName; static const char* _imageDensityName; static const char* _frontalOverlapName; static const char* _sideOverlapName; static const char* _adjustedFootprintSideName; static const char* _adjustedFootprintFrontalName; + static const char* _jsonCameraNameKey; + + // The following are deprecated usage and only included in order to convert older formats + + enum CameraSpecType { + CameraSpecNone, + CameraSpecCustom, + CameraSpecKnown + }; + static const char* _jsonCameraSpecTypeKey; - static const char* _jsonKnownCameraNameKey; }; diff --git a/src/MissionManager/CameraCalcTest.cc b/src/MissionManager/CameraCalcTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..cb44927c36a92ccb2994d10a0c50708865bad72c --- /dev/null +++ b/src/MissionManager/CameraCalcTest.cc @@ -0,0 +1,91 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "CameraCalcTest.h" +#include "QGCApplication.h" + +CameraCalcTest::CameraCalcTest(void) + : _offlineVehicle(NULL) +{ + +} + +void CameraCalcTest::init(void) +{ + UnitTest::init(); + + _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); + _cameraCalc = new CameraCalc(_offlineVehicle, this); + + _rgSignals[cameraNameChangedIndex] = SIGNAL(cameraNameChanged(QString)); + _rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); + _rgSignals[imageFootprintSideChangedIndex] = SIGNAL(imageFootprintSideChanged(double)); + _rgSignals[imageFootprintFrontalChangedIndex] = SIGNAL(imageFootprintFrontalChanged(double)); + _rgSignals[distanceToSurfaceRelativeChangedIndex] = SIGNAL(distanceToSurfaceRelativeChanged(bool)); + + _multiSpy = new MultiSignalSpy(); + QCOMPARE(_multiSpy->init(_cameraCalc, _rgSignals, _cSignals), true); +} + +void CameraCalcTest::cleanup(void) +{ + delete _cameraCalc; + delete _offlineVehicle; + delete _multiSpy; +} + +void CameraCalcTest::_testDirty(void) +{ + QVERIFY(!_cameraCalc->dirty()); + _cameraCalc->setDirty(false); + QVERIFY(!_cameraCalc->dirty()); + QVERIFY(_multiSpy->checkNoSignals()); + + _cameraCalc->setDirty(true); + QVERIFY(_cameraCalc->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _multiSpy->clearAllSignals(); + + _cameraCalc->setDirty(false); + QVERIFY(!_cameraCalc->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + _multiSpy->clearAllSignals(); + + // These facts should set dirty when changed + QList rgFacts; + rgFacts << _cameraCalc->valueSetIsDistance() + << _cameraCalc->distanceToSurface() + << _cameraCalc->imageDensity() + << _cameraCalc->frontalOverlap () + << _cameraCalc->sideOverlap () + << _cameraCalc->adjustedFootprintSide() + << _cameraCalc->adjustedFootprintFrontal(); + foreach(Fact* fact, rgFacts) { + qDebug() << fact->name(); + QVERIFY(!_cameraCalc->dirty()); + if (fact->typeIsBool()) { + fact->setRawValue(!fact->rawValue().toBool()); + } else { + fact->setRawValue(fact->rawValue().toDouble() + 1); + } + QVERIFY(_multiSpy->checkSignalByMask(dirtyChangedMask)); + _cameraCalc->setDirty(false); + _multiSpy->clearAllSignals(); + } + rgFacts.clear(); + + _cameraCalc->setCameraName(_cameraCalc->customCameraName()); + QVERIFY(_cameraCalc->dirty()); + _multiSpy->clearAllSignals(); + + _cameraCalc->setDistanceToSurfaceRelative(!_cameraCalc->distanceToSurfaceRelative()); + QVERIFY(_cameraCalc->dirty()); + _multiSpy->clearAllSignals(); +} diff --git a/src/MissionManager/CameraCalcTest.h b/src/MissionManager/CameraCalcTest.h new file mode 100644 index 0000000000000000000000000000000000000000..02fa9b723a607a7d280da065ad7138836539baf1 --- /dev/null +++ b/src/MissionManager/CameraCalcTest.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "UnitTest.h" +#include "MultiSignalSpy.h" +#include "CameraCalc.h" + +#include + +class CameraCalcTest : public UnitTest +{ + Q_OBJECT + +public: + CameraCalcTest(void); + +protected: + void init(void) final; + void cleanup(void) final; + +private slots: + void _testDirty(void); + +private: + enum { + cameraNameChangedIndex = 0, + dirtyChangedIndex, + imageFootprintSideChangedIndex, + imageFootprintFrontalChangedIndex, + distanceToSurfaceRelativeChangedIndex, + maxSignalIndex + }; + + enum { + cameraNameChangedMask = 1 << cameraNameChangedIndex, + dirtyChangedMask = 1 << dirtyChangedIndex, + imageFootprintSideChangedMask = 1 << imageFootprintSideChangedIndex, + imageFootprintFrontalChangedMask = 1 << imageFootprintFrontalChangedIndex, + distanceToSurfaceRelativeChangedMask = 1 << distanceToSurfaceRelativeChangedIndex, + }; + + static const size_t _cSignals = maxSignalIndex; + const char* _rgSignals[_cSignals]; + + Vehicle* _offlineVehicle; + MultiSignalSpy* _multiSpy; + CameraCalc* _cameraCalc; +}; diff --git a/src/MissionManager/CameraSection.cc b/src/MissionManager/CameraSection.cc index b6a2a4d341e8d28777767983b92f341464bd526e..f16d54aaf1a111023475cb317f1b2bfe0732dd9c 100644 --- a/src/MissionManager/CameraSection.cc +++ b/src/MissionManager/CameraSection.cc @@ -59,8 +59,8 @@ CameraSection::CameraSection(Vehicle* vehicle, QObject* parent) connect(&_cameraActionFact, &Fact::valueChanged, this, &CameraSection::_cameraActionChanged); - connect(&_gimbalPitchFact, &Fact::valueChanged, this, &CameraSection::_setDirty); - connect(&_gimbalYawFact, &Fact::valueChanged, this, &CameraSection::_setDirty); + connect(&_gimbalPitchFact, &Fact::valueChanged, this, &CameraSection::_dirtyIfSpecified); + connect(&_gimbalYawFact, &Fact::valueChanged, this, &CameraSection::_dirtyIfSpecified); connect(&_cameraPhotoIntervalDistanceFact, &Fact::valueChanged, this, &CameraSection::_setDirty); connect(&_cameraPhotoIntervalTimeFact, &Fact::valueChanged, this, &CameraSection::_setDirty); connect(&_cameraModeFact, &Fact::valueChanged, this, &CameraSection::_setDirty); @@ -68,7 +68,9 @@ CameraSection::CameraSection(Vehicle* vehicle, QObject* parent) connect(this, &CameraSection::specifyCameraModeChanged, this, &CameraSection::_setDirty); connect(this, &CameraSection::specifyGimbalChanged, this, &CameraSection::_updateSpecifiedGimbalYaw); + connect(this, &CameraSection::specifyGimbalChanged, this, &CameraSection::_updateSpecifiedGimbalPitch); connect(&_gimbalYawFact, &Fact::valueChanged, this, &CameraSection::_updateSpecifiedGimbalYaw); + connect(&_gimbalPitchFact, &Fact::valueChanged, this, &CameraSection::_updateSpecifiedGimbalPitch); } void CameraSection::setSpecifyGimbal(bool specifyGimbal) @@ -491,9 +493,23 @@ double CameraSection::specifiedGimbalYaw(void) const return _specifyGimbal ? _gimbalYawFact.rawValue().toDouble() : std::numeric_limits::quiet_NaN(); } +double CameraSection::specifiedGimbalPitch(void) const +{ + return _specifyGimbal ? _gimbalPitchFact.rawValue().toDouble() : std::numeric_limits::quiet_NaN(); +} + void CameraSection::_updateSpecifiedGimbalYaw(void) { - emit specifiedGimbalYawChanged(specifiedGimbalYaw()); + if (_specifyGimbal) { + emit specifiedGimbalYawChanged(specifiedGimbalYaw()); + } +} + +void CameraSection::_updateSpecifiedGimbalPitch(void) +{ + if (_specifyGimbal) { + emit specifiedGimbalPitchChanged(specifiedGimbalPitch()); + } } void CameraSection::_updateSettingsSpecified(void) @@ -521,3 +537,11 @@ bool CameraSection::cameraModeSupported(void) const { return _vehicle->firmwarePlugin()->supportedMissionCommands().contains(MAV_CMD_SET_CAMERA_MODE); } + +void CameraSection::_dirtyIfSpecified(void) +{ + // We only set the dirty bit if specify gimbal it set. This allows us to change defaults without affecting dirty. + if (_specifyGimbal) { + setDirty(true); + } +} diff --git a/src/MissionManager/CameraSection.h b/src/MissionManager/CameraSection.h index 862354ae9b4f2b576e28e091ce46a9e903990bb6..32d019e5efeb414cee187747aae6811571c09a57 100644 --- a/src/MissionManager/CameraSection.h +++ b/src/MissionManager/CameraSection.h @@ -61,6 +61,10 @@ public: ///< @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; + // Overrides from Section bool available (void) const override { return _available; } bool dirty (void) const override { return _dirty; } @@ -75,14 +79,17 @@ signals: bool specifyGimbalChanged (bool specifyGimbal); bool specifyCameraModeChanged (bool specifyCameraMode); void specifiedGimbalYawChanged (double gimbalYaw); + void specifiedGimbalPitchChanged(double gimbalPitch); private slots: void _setDirty(void); void _setDirtyAndUpdateItemCount(void); void _updateSpecifiedGimbalYaw(void); + void _updateSpecifiedGimbalPitch(void); void _specifyChanged(void); void _updateSettingsSpecified(void); void _cameraActionChanged(void); + void _dirtyIfSpecified(void); private: bool _scanGimbal(QmlObjectListModel* visualItems, int scanIndex); diff --git a/src/MissionManager/CameraSectionTest.cc b/src/MissionManager/CameraSectionTest.cc index 3408a23784f2555c8055eaddd7d2190c5271fc55..69eb38db70fa25f201c1ea9547f27a4024dcfdb5 100644 --- a/src/MissionManager/CameraSectionTest.cc +++ b/src/MissionManager/CameraSectionTest.cc @@ -36,6 +36,7 @@ void CameraSectionTest::init(void) rgCameraSignals[specifyGimbalChangedIndex] = SIGNAL(specifyGimbalChanged(bool)); rgCameraSignals[specifiedGimbalYawChangedIndex] = SIGNAL(specifiedGimbalYawChanged(double)); + rgCameraSignals[specifiedGimbalPitchChangedIndex] = SIGNAL(specifiedGimbalPitchChanged(double)); rgCameraSignals[specifyCameraModeChangedIndex] = SIGNAL(specifyCameraModeChanged(bool)); _cameraSection = _simpleItem->cameraSection(); @@ -45,12 +46,15 @@ void CameraSectionTest::init(void) QVERIFY(_spySection); _validGimbalItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_DO_MOUNT_CONTROL, MAV_FRAME_MISSION, 10.1234, 0, 20.1234, 0, 0, 0, MAV_MOUNT_MODE_MAVLINK_TARGETING, true, false), this); _validTimeItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_IMAGE_START_CAPTURE, MAV_FRAME_MISSION, 0, 48, 0, NAN, NAN, NAN, NAN, true, false), this); _validDistanceItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_DO_SET_CAM_TRIGG_DIST, MAV_FRAME_MISSION, @@ -61,6 +65,7 @@ void CameraSectionTest::init(void) true, false), this); _validStartVideoItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, // sequence number MAV_CMD_VIDEO_START_CAPTURE, MAV_FRAME_MISSION, @@ -71,15 +76,19 @@ void CameraSectionTest::init(void) false), // isCurrentItem this); _validStopVideoItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_VIDEO_STOP_CAPTURE, MAV_FRAME_MISSION, 0, NAN, NAN, NAN, NAN, NAN, NAN, true, false), this); _validStopDistanceItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_DO_SET_CAM_TRIGG_DIST, MAV_FRAME_MISSION, 0, 0, 0, 0, 0, 0, 0, true, false), this); _validStopTimeItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(1, MAV_CMD_IMAGE_STOP_CAPTURE, MAV_FRAME_MISSION, 0, NAN, NAN, NAN, NAN, NAN, NAN, true, false), this); _validCameraPhotoModeItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, // sequence number MAV_CMD_SET_CAMERA_MODE, MAV_FRAME_MISSION, @@ -90,6 +99,7 @@ void CameraSectionTest::init(void) false), // isCurrentItem this); _validCameraVideoModeItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, // sequence number MAV_CMD_SET_CAMERA_MODE, MAV_FRAME_MISSION, @@ -100,6 +110,7 @@ void CameraSectionTest::init(void) false), // isCurrentItem this); _validCameraSurveyPhotoModeItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, // sequence number MAV_CMD_SET_CAMERA_MODE, MAV_FRAME_MISSION, @@ -110,6 +121,7 @@ void CameraSectionTest::init(void) false), // isCurrentItem this); _validTakePhotoItem = new SimpleMissionItem(_offlineVehicle, + true, // editMode MissionItem(0, MAV_CMD_IMAGE_START_CAPTURE, MAV_FRAME_MISSION, @@ -206,22 +218,32 @@ void CameraSectionTest::_testDirty(void) _cameraSection->setDirty(false); _spySection->clearAllSignals(); - // Check the remaining items that should set dirty bit + // dirty SHOULD NOT change if pitch or yaw is changed while specifyGimbal IS NOT set + _cameraSection->setSpecifyGimbal(false); + _cameraSection->setDirty(false); + _spySection->clearAllSignals(); + _cameraSection->gimbalPitch()->setRawValue(_cameraSection->gimbalPitch()->rawValue().toDouble() + 1); + _cameraSection->gimbalYaw()->setRawValue(_cameraSection->gimbalYaw()->rawValue().toDouble() + 1); + QVERIFY(_spySection->checkNoSignalByMask(dirtyChangedMask)); + QCOMPARE(_cameraSection->dirty(), false); + // dirty SHOULD change if pitch or yaw is changed while specifyGimbal IS set + _cameraSection->setSpecifyGimbal(true); + _cameraSection->setDirty(false); + _spySection->clearAllSignals(); _cameraSection->gimbalPitch()->setRawValue(_cameraSection->gimbalPitch()->rawValue().toDouble() + 1); QVERIFY(_spySection->checkSignalByMask(dirtyChangedMask)); - QCOMPARE(_spySection->pullBoolFromSignalIndex(dirtyChangedIndex), true); QCOMPARE(_cameraSection->dirty(), true); _cameraSection->setDirty(false); _spySection->clearAllSignals(); - - _cameraSection->gimbalYaw()->setRawValue(_cameraSection->gimbalPitch()->rawValue().toDouble() + 1); + _cameraSection->gimbalYaw()->setRawValue(_cameraSection->gimbalYaw()->rawValue().toDouble() + 1); QVERIFY(_spySection->checkSignalByMask(dirtyChangedMask)); - QCOMPARE(_spySection->pullBoolFromSignalIndex(dirtyChangedIndex), true); QCOMPARE(_cameraSection->dirty(), true); _cameraSection->setDirty(false); _spySection->clearAllSignals(); + // Check the remaining items that should set dirty bit + _cameraSection->cameraAction()->setRawValue(_cameraSection->cameraAction()->rawValue().toInt() + 1); QVERIFY(_spySection->checkSignalByMask(dirtyChangedMask)); QCOMPARE(_spySection->pullBoolFromSignalIndex(dirtyChangedIndex), true); @@ -340,7 +362,7 @@ void CameraSectionTest::_checkAvailable(void) 70.1234567, true, // autoContinue false); // isCurrentItem - SimpleMissionItem* item = new SimpleMissionItem(_offlineVehicle, missionItem); + SimpleMissionItem* item = new SimpleMissionItem(_offlineVehicle, true /* editMode */, missionItem); QVERIFY(item->cameraSection()); QCOMPARE(item->cameraSection()->available(), false); } @@ -611,7 +633,7 @@ void CameraSectionTest::_testScanForGimbalSection(void) // Gimbal command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validGimbalItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validGimbalItem->missionItem()); invalidSimpleItem.missionItem().setParam2(10); // roll is not supported visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -701,7 +723,7 @@ void CameraSectionTest::_testScanForCameraModeSection(void) */ // Mode command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validCameraPhotoModeItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validCameraPhotoModeItem->missionItem()); invalidSimpleItem.missionItem().setParam3(1); // Param3 should be NaN visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -740,7 +762,7 @@ void CameraSectionTest::_testScanForPhotoIntervalTimeSection(void) // Image start command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validTimeItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validTimeItem->missionItem()); invalidSimpleItem.missionItem().setParam3(10); // must be 0 for unlimited visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -781,7 +803,7 @@ void CameraSectionTest::_testScanForPhotoIntervalDistanceSection(void) // Trigger distance command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validDistanceItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validDistanceItem->missionItem()); invalidSimpleItem.missionItem().setParam1(-1); // must be >= 0 visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -866,7 +888,7 @@ void CameraSectionTest::_testScanForStartVideoSection(void) // Start Video command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validStartVideoItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validStartVideoItem->missionItem()); invalidSimpleItem.missionItem().setParam1(10); // Reserved (must be 0) visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -909,7 +931,7 @@ void CameraSectionTest::_testScanForStopVideoSection(void) // Trigger distance command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validStopVideoItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validStopVideoItem->missionItem()); invalidSimpleItem.missionItem().setParam1(10); // must be 0 visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -982,7 +1004,7 @@ void CameraSectionTest::_testScanForTakePhotoSection(void) // Take Photo command but incorrect settings - SimpleMissionItem invalidSimpleItem(_offlineVehicle, _validTimeItem->missionItem()); + SimpleMissionItem invalidSimpleItem(_offlineVehicle, true /* editMode */, _validTimeItem->missionItem()); invalidSimpleItem.missionItem().setParam3(10); // must be 1 for single photo visualItems.append(&invalidSimpleItem); QCOMPARE(_cameraSection->scanForSection(&visualItems, scanIndex), false); @@ -1081,3 +1103,23 @@ void CameraSectionTest::_testScanForMultipleItems(void) } } } + +void CameraSectionTest::_testSpecifiedGimbalValuesChanged(void) +{ + // specifiedGimbal[Yaw|Pitch]Changed SHOULD NOT signal if values are changed when specifyGimbal IS NOT set + _cameraSection->setSpecifyGimbal(false); + _spyCamera->clearAllSignals(); + _cameraSection->gimbalYaw()->setRawValue(_cameraSection->gimbalYaw()->rawValue().toDouble() + 1); + QVERIFY(_spyCamera->checkNoSignalByMask(specifiedGimbalYawChangedMask)); + _cameraSection->gimbalPitch()->setRawValue(_cameraSection->gimbalPitch()->rawValue().toDouble() + 1); + QVERIFY(_spyCamera->checkNoSignalByMask(specifiedGimbalPitchChangedMask)); + + // specifiedGimbal[Yaw|Pitch]Changed SHOULD signal if values are changed when specifyGimbal IS set + _cameraSection->setSpecifyGimbal(true); + _spyCamera->clearAllSignals(); + _cameraSection->gimbalYaw()->setRawValue(_cameraSection->gimbalYaw()->rawValue().toDouble() + 1); + QVERIFY(_spyCamera->checkSignalByMask(specifiedGimbalYawChangedMask)); + _spyCamera->clearAllSignals(); + _cameraSection->gimbalPitch()->setRawValue(_cameraSection->gimbalPitch()->rawValue().toDouble() + 1); + QVERIFY(_spyCamera->checkSignalByMask(specifiedGimbalPitchChangedMask)); +} diff --git a/src/MissionManager/CameraSectionTest.h b/src/MissionManager/CameraSectionTest.h index 0ca4ffdd03f55f240b2bbf6265ae4f48cd9b5e7d..5e824a8fb720cd5b4cf9de32a1e36cd93f58c343 100644 --- a/src/MissionManager/CameraSectionTest.h +++ b/src/MissionManager/CameraSectionTest.h @@ -24,20 +24,21 @@ public: void cleanup(void) override; private slots: - void _testDirty(void); - void _testSettingsAvailable(void); - void _checkAvailable(void); - void _testItemCount(void); - void _testAppendSectionItems(void); - void _testScanForGimbalSection(void); - void _testScanForPhotoIntervalTimeSection(void); - void _testScanForPhotoIntervalDistanceSection(void); - void _testScanForStartVideoSection(void); - void _testScanForStopVideoSection(void); - void _testScanForStopImageSection(void); - void _testScanForCameraModeSection(void); - void _testScanForTakePhotoSection(void); - void _testScanForMultipleItems(void); + void _testDirty (void); + void _testSettingsAvailable (void); + void _checkAvailable (void); + void _testItemCount (void); + void _testAppendSectionItems (void); + void _testScanForGimbalSection (void); + void _testScanForPhotoIntervalTimeSection (void); + void _testScanForPhotoIntervalDistanceSection (void); + void _testScanForStartVideoSection (void); + void _testScanForStopVideoSection (void); + void _testScanForStopImageSection (void); + void _testScanForCameraModeSection (void); + void _testScanForTakePhotoSection (void); + void _testScanForMultipleItems (void); + void _testSpecifiedGimbalValuesChanged (void); private: void _createSpy(CameraSection* cameraSection, MultiSignalSpy** cameraSpy); @@ -47,14 +48,16 @@ private: enum { specifyGimbalChangedIndex = 0, specifiedGimbalYawChangedIndex, + specifiedGimbalPitchChangedIndex, specifyCameraModeChangedIndex, maxSignalIndex, }; enum { - specifyGimbalChangedMask = 1 << specifyGimbalChangedIndex, - specifiedGimbalYawChangedMask = 1 << specifiedGimbalYawChangedIndex, - specifyCameraModeChangedMask = 1 << specifyCameraModeChangedIndex, + specifyGimbalChangedMask = 1 << specifyGimbalChangedIndex, + specifiedGimbalYawChangedMask = 1 << specifiedGimbalYawChangedIndex, + specifiedGimbalPitchChangedMask = 1 << specifiedGimbalPitchChangedIndex, + specifyCameraModeChangedMask = 1 << specifyCameraModeChangedIndex, }; static const size_t cCameraSignals = maxSignalIndex; diff --git a/src/MissionManager/ComplexMissionItem.h b/src/MissionManager/ComplexMissionItem.h index 10ae81c92314137999302b2ef05c67095a5d5336..bc27fe7f80856a77f750925fb21b734bcc5fab0a 100644 --- a/src/MissionManager/ComplexMissionItem.h +++ b/src/MissionManager/ComplexMissionItem.h @@ -47,9 +47,9 @@ public: static const char* jsonComplexItemTypeKey; signals: - void complexDistanceChanged (double complexDistance); + void complexDistanceChanged (void); void greatestDistanceToChanged (void); - void additionalTimeDelayChanged (double additionalTimeDelay); + void additionalTimeDelayChanged (void); }; #endif diff --git a/src/MissionManager/CorridorScan.SettingsGroup.json b/src/MissionManager/CorridorScan.SettingsGroup.json new file mode 100644 index 0000000000000000000000000000000000000000..78e0a7bb017ce004f72a8829ae7aa6cf3efac4d4 --- /dev/null +++ b/src/MissionManager/CorridorScan.SettingsGroup.json @@ -0,0 +1,46 @@ +[ +{ + "name": "Altitude", + "shortDescription": "Altitude for the bottom layer of the structure scan.", + "type": "double", + "units": "m", + "decimalPlaces": 1, + "defaultValue": 50 +}, +{ + "name": "CorridorWidth", + "shortDescription": "Corridor width. Specify 0 width for a single pass scan.", + "type": "double", + "units": "m", + "min": 0, + "decimalPlaces": 1, + "defaultValue": 50 +}, +{ + "name": "Trigger distance", + "shortDescription": "Distance between each triggering of the camera. 0 specifies not camera trigger.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 25 +}, +{ + "name": "GridSpacing", + "shortDescription": "Amount of spacing in between parallel grid lines.", + "type": "double", + "decimalPlaces": 2, + "min": 0.1, + "units": "m", + "defaultValue": 30 +}, +{ + "name": "TurnaroundDistance", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turnaround.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 30 +} +] diff --git a/src/MissionManager/CorridorScanComplexItem.cc b/src/MissionManager/CorridorScanComplexItem.cc new file mode 100644 index 0000000000000000000000000000000000000000..2a93b928bd2eb8bc704f2aa1cd7979e0281fce4c --- /dev/null +++ b/src/MissionManager/CorridorScanComplexItem.cc @@ -0,0 +1,464 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "CorridorScanComplexItem.h" +#include "JsonHelper.h" +#include "MissionController.h" +#include "QGCGeo.h" +#include "QGroundControlQmlGlobal.h" +#include "QGCQGeoCoordinate.h" +#include "SettingsManager.h" +#include "AppSettings.h" +#include "QGCQGeoCoordinate.h" + +#include + +QGC_LOGGING_CATEGORY(CorridorScanComplexItemLog, "CorridorScanComplexItemLog") + +const char* CorridorScanComplexItem::settingsGroup = "CorridorScan"; +const char* CorridorScanComplexItem::corridorWidthName = "CorridorWidth"; +const char* CorridorScanComplexItem::_entryPointName = "EntryPoint"; + +const char* CorridorScanComplexItem::jsonComplexItemTypeValue = "CorridorScan"; + +CorridorScanComplexItem::CorridorScanComplexItem(Vehicle* vehicle, QObject* parent) + : TransectStyleComplexItem (vehicle, settingsGroup, parent) + , _ignoreRecalc (false) + , _entryPoint (0) + , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/CorridorScan.SettingsGroup.json"), this)) + , _corridorWidthFact (settingsGroup, _metaDataMap[corridorWidthName]) +{ + _editorQml = "qrc:/qml/CorridorScanEditor.qml"; + + // We override the altitude to the mission default + if (_cameraCalc.isManualCamera() || !_cameraCalc.valueSetIsDistance()->rawValue().toBool()) { + _cameraCalc.distanceToSurface()->setRawValue(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue()); + } + + connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_setDirty); + connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_setDirty); + + connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &CorridorScanComplexItem::coordinateHasRelativeAltitudeChanged); + connect(&_cameraCalc, &CameraCalc::distanceToSurfaceRelativeChanged, this, &CorridorScanComplexItem::exitCoordinateHasRelativeAltitudeChanged); + + connect(&_corridorPolyline, &QGCMapPolyline::dirtyChanged, this, &CorridorScanComplexItem::_polylineDirtyChanged); + connect(&_corridorPolyline, &QGCMapPolyline::countChanged, this, &CorridorScanComplexItem::_polylineCountChanged); + + connect(&_corridorPolyline, &QGCMapPolyline::pathChanged, this, &CorridorScanComplexItem::_rebuildCorridor); + connect(&_corridorWidthFact, &Fact::valueChanged, this, &CorridorScanComplexItem::_rebuildCorridor); + + _rebuildCorridor(); +} + +void CorridorScanComplexItem::_polylineCountChanged(int count) +{ + Q_UNUSED(count); + emit lastSequenceNumberChanged(lastSequenceNumber()); +} + +int CorridorScanComplexItem::lastSequenceNumber(void) const +{ + int itemCount = _transectPoints.count(); // Each transpect point represents a waypoint item + + if (_cameraTriggerInTurnAroundFact.rawValue().toBool()) { + // Only one camera start and on camera stop + itemCount += 2; + } else { + // Each transect will have a camera start and stop in it + itemCount += _transectCount() * 2; + } + + return _sequenceNumber + itemCount - 1; +} + +void CorridorScanComplexItem::save(QJsonArray& missionItems) +{ + QJsonObject saveObject; + + saveObject[JsonHelper::jsonVersionKey] = 1; + saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; + saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; + saveObject[corridorWidthName] = _corridorWidthFact.rawValue().toDouble(); + saveObject[turnAroundDistanceName] = _turnAroundDistanceFact.rawValue().toDouble(); + saveObject[_entryPointName] = _entryPoint; + + QJsonObject cameraCalcObject; + _cameraCalc.save(cameraCalcObject); + saveObject[_jsonCameraCalcKey] = cameraCalcObject; + + _corridorPolyline.saveToJson(saveObject); + + _save(saveObject); + + missionItems.append(saveObject); +} + +bool CorridorScanComplexItem::load(const QJsonObject& complexObject, int sequenceNumber, QString& errorString) +{ + QList keyInfoList = { + { JsonHelper::jsonVersionKey, QJsonValue::Double, true }, + { VisualMissionItem::jsonTypeKey, QJsonValue::String, true }, + { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, + { corridorWidthName, QJsonValue::Double, true }, + { turnAroundDistanceName, QJsonValue::Double, true }, + { _entryPointName, QJsonValue::Double, true }, + { QGCMapPolyline::jsonPolylineKey, QJsonValue::Array, true }, + { _jsonCameraCalcKey, QJsonValue::Object, true }, + }; + if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { + return false; + } + + if (!_corridorPolyline.loadFromJson(complexObject, true, errorString)) { + return false; + } + + QString itemType = complexObject[VisualMissionItem::jsonTypeKey].toString(); + QString complexType = complexObject[ComplexMissionItem::jsonComplexItemTypeKey].toString(); + if (itemType != VisualMissionItem::jsonTypeComplexItemValue || complexType != jsonComplexItemTypeValue) { + errorString = tr("%1 does not support loading this complex mission item type: %2:%3").arg(qgcApp()->applicationName()).arg(itemType).arg(complexType); + return false; + } + + int version = complexObject[JsonHelper::jsonVersionKey].toInt(); + if (version != 1) { + errorString = tr("%1 complex item version %2 not supported").arg(jsonComplexItemTypeValue).arg(version); + return false; + } + + setSequenceNumber(sequenceNumber); + + if (!_load(complexObject, errorString)) { + return false; + } + + _corridorWidthFact.setRawValue (complexObject[corridorWidthName].toDouble()); + + _entryPoint = complexObject[_entryPointName].toInt(); + + _rebuildCorridor(); + + return true; +} + +bool CorridorScanComplexItem::specifiesCoordinate(void) const +{ + return _corridorPolyline.count() > 1; +} + +int CorridorScanComplexItem::_transectCount(void) const +{ + double transectSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); + double fullWidth = _corridorWidthFact.rawValue().toDouble(); + return fullWidth > 0.0 ? qCeil(fullWidth / transectSpacing) : 1; +} + +void CorridorScanComplexItem::appendMissionItems(QList& items, QObject* missionItemParent) +{ + int seqNum = _sequenceNumber; + int pointIndex = 0; + bool imagesEverywhere = _cameraTriggerInTurnAroundFact.rawValue().toBool(); + + while (pointIndex < _transectPoints.count()) { + if (_hasTurnaround()) { + QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, + 0, // No hold time + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + vertexCoord.latitude(), + vertexCoord.longitude(), + _cameraCalc.distanceToSurface()->rawValue().toDouble(), // Altitude + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + if (imagesEverywhere && pointIndex == 1) { + item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_CAM_TRIGG_DIST, + MAV_FRAME_MISSION, + _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble(), // trigger distance + 0, // shutter integration (ignore) + 1, // trigger immediately when starting + 0, 0, 0, 0, // param 4-7 unused + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } + } + + bool addTrigger = imagesEverywhere ? false : true; + for (int i=0; i<_corridorPolyline.count(); i++) { + QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, + 0, // No hold time + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + vertexCoord.latitude(), + vertexCoord.longitude(), + _cameraCalc.distanceToSurface()->rawValue().toDouble(), // Altitude + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + + if (addTrigger) { + addTrigger = false; + item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_CAM_TRIGG_DIST, + MAV_FRAME_MISSION, + _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble(), // trigger distance + 0, // shutter integration (ignore) + 1, // trigger immediately when starting + 0, 0, 0, 0, // param 4-7 unused + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } + } + + if (!imagesEverywhere) { + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_CAM_TRIGG_DIST, + MAV_FRAME_MISSION, + 0, // stop triggering + 0, // shutter integration (ignore) + 0, // trigger immediately when starting + 0, 0, 0, 0, // param 4-7 unused + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } + + if (_hasTurnaround()) { + QGeoCoordinate vertexCoord = _transectPoints[pointIndex++].value(); + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_NAV_WAYPOINT, + _cameraCalc.distanceToSurfaceRelative() ? MAV_FRAME_GLOBAL_RELATIVE_ALT : MAV_FRAME_GLOBAL, + 0, // No hold time + 0.0, // No acceptance radius specified + 0.0, // Pass through waypoint + std::numeric_limits::quiet_NaN(), // Yaw unchanged + vertexCoord.latitude(), + vertexCoord.longitude(), + _cameraCalc.distanceToSurface()->rawValue().toDouble(), // Altitude + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } + } + + if (imagesEverywhere) { + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_CAM_TRIGG_DIST, + MAV_FRAME_MISSION, + 0, // stop triggering + 0, // shutter integration (ignore) + 0, // trigger immediately when starting + 0, 0, 0, 0, // param 4-7 unused + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); + } +} + +void CorridorScanComplexItem::applyNewAltitude(double newAltitude) +{ + _cameraCalc.valueSetIsDistance()->setRawValue(true); + _cameraCalc.distanceToSurface()->setRawValue(newAltitude); + _cameraCalc.setDistanceToSurfaceRelative(true); +} + +void CorridorScanComplexItem::_polylineDirtyChanged(bool dirty) +{ + if (dirty) { + setDirty(true); + } +} + +void CorridorScanComplexItem::rotateEntryPoint(void) +{ + _entryPoint++; + if (_entryPoint > 3) { + _entryPoint = 0; + } + + _rebuildCorridor(); +} + +void CorridorScanComplexItem::_rebuildCorridorPolygon(void) +{ + if (_corridorPolyline.count() < 2) { + _surveyAreaPolygon.clear(); + return; + } + + double halfWidth = _corridorWidthFact.rawValue().toDouble() / 2.0; + + QList firstSideVertices = _corridorPolyline.offsetPolyline(halfWidth); + QList secondSideVertices = _corridorPolyline.offsetPolyline(-halfWidth); + + _surveyAreaPolygon.clear(); + foreach (const QGeoCoordinate& vertex, firstSideVertices) { + _surveyAreaPolygon.appendVertex(vertex); + } + for (int i=secondSideVertices.count() - 1; i >= 0; i--) { + _surveyAreaPolygon.appendVertex(secondSideVertices[i]); + } +} + +void CorridorScanComplexItem::_rebuildTransects(void) +{ + _transectPoints.clear(); + _cameraShots = 0; + + double transectSpacing = _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); + double fullWidth = _corridorWidthFact.rawValue().toDouble(); + double halfWidth = fullWidth / 2.0; + int transectCount = _transectCount(); + double normalizedTransectPosition = transectSpacing / 2.0; + + if (_corridorPolyline.count() >= 2) { + int singleTransectImageCount = qCeil(_corridorPolyline.length() / _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); + + // First build up the transects all going the same direction + QList> transects; + for (int i=0; i transect = _corridorPolyline.offsetPolyline(offsetDistance); + if (_hasTurnaround()) { + QGeoCoordinate extensionCoord; + + // Extend the transect ends for turnaround + double azimuth = transect[0].azimuthTo(transect[1]); + extensionCoord = transect[0].atDistanceAndAzimuth(-_turnAroundDistanceFact.rawValue().toDouble(), azimuth); + transect.prepend(extensionCoord); + azimuth = transect.last().azimuthTo(transect[transect.count() - 2]); + extensionCoord = transect.last().atDistanceAndAzimuth(-_turnAroundDistanceFact.rawValue().toDouble(), azimuth); + transect.append(extensionCoord); + } + + transects.append(transect); + normalizedTransectPosition += transectSpacing; + } + + // Now deal with fixing up the entry point: + // 0: Leave alone + // 1: Start at same end, opposite side of center + // 2: Start at opposite end, same side + // 3: Start at opposite end, opposite side + + bool reverseTransects = false; + bool reverseVertices = false; + switch (_entryPoint) { + case 0: + reverseTransects = false; + reverseVertices = false; + break; + case 1: + reverseTransects = true; + reverseVertices = false; + break; + case 2: + reverseTransects = false; + reverseVertices = true; + break; + case 3: + reverseTransects = true; + reverseVertices = true; + break; + } + if (reverseTransects) { + QList> reversedTransects; + foreach (const QList& transect, transects) { + reversedTransects.prepend(transect); + } + transects = reversedTransects; + } + if (reverseVertices) { + for (int i=0; i reversedVertices; + foreach (const QGeoCoordinate& vertex, transects[i]) { + reversedVertices.prepend(vertex); + } + transects[i] = reversedVertices; + } + } + + // Convert the list of transects to grid points + reverseVertices = false; + for (int i=0; i transectVertices = transects[i]; + if (reverseVertices) { + reverseVertices = false; + QList reversedVertices; + for (int j=transectVertices.count()-1; j>=0; j--) { + reversedVertices.append(transectVertices[j]); + } + transectVertices = reversedVertices; + } else { + reverseVertices = true; + } + for (int i=0; i().distanceTo(_transectPoints[i+1].value()); + } + + if (_cameraTriggerInTurnAroundFact.rawValue().toDouble()) { + _cameraShots = qCeil(_complexDistance / _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); + } + + _coordinate = _transectPoints.count() ? _transectPoints.first().value() : QGeoCoordinate(); + _exitCoordinate = _transectPoints.count() ? _transectPoints.last().value() : QGeoCoordinate(); + + emit transectPointsChanged(); + emit cameraShotsChanged(); + emit complexDistanceChanged(); + emit coordinateChanged(_coordinate); + emit exitCoordinateChanged(_exitCoordinate); +} + +void CorridorScanComplexItem::_rebuildCorridor(void) +{ + _rebuildCorridorPolygon(); + _rebuildTransects(); +} diff --git a/src/MissionManager/CorridorScanComplexItem.h b/src/MissionManager/CorridorScanComplexItem.h new file mode 100644 index 0000000000000000000000000000000000000000..01c1983ff05f465ee1dbb8e084a62570cafb03b9 --- /dev/null +++ b/src/MissionManager/CorridorScanComplexItem.h @@ -0,0 +1,79 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "TransectStyleComplexItem.h" +#include "MissionItem.h" +#include "SettingsFact.h" +#include "QGCLoggingCategory.h" +#include "QGCMapPolyline.h" +#include "QGCMapPolygon.h" +#include "CameraCalc.h" + +Q_DECLARE_LOGGING_CATEGORY(CorridorScanComplexItemLog) + +class CorridorScanComplexItem : public TransectStyleComplexItem +{ + Q_OBJECT + +public: + CorridorScanComplexItem(Vehicle* vehicle, QObject* parent = NULL); + + Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) + Q_PROPERTY(QGCMapPolyline* corridorPolyline READ corridorPolyline CONSTANT) + Q_PROPERTY(Fact* corridorWidth READ corridorWidth CONSTANT) + + Fact* corridorWidth (void) { return &_corridorWidthFact; } + QGCMapPolyline* corridorPolyline(void) { return &_corridorPolyline; } + + Q_INVOKABLE void rotateEntryPoint(void); + + // Overrides from ComplexMissionItem + + int lastSequenceNumber (void) const final; + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final; + QString mapVisualQML (void) const final { return QStringLiteral("CorridorScanMapVisual.qml"); } + + // Overrides from TransectStyleComplexItem + + void save (QJsonArray& missionItems) final; + bool specifiesCoordinate (void) const final; + void appendMissionItems (QList& items, QObject* missionItemParent) final; + void applyNewAltitude (double newAltitude) final; + + static const char* jsonComplexItemTypeValue; + + static const char* settingsGroup; + static const char* corridorWidthName; + +private slots: + void _polylineDirtyChanged (bool dirty); + void _polylineCountChanged (int count); + void _rebuildCorridor (void); + + // Overrides from TransectStyleComplexItem + virtual void _rebuildTransects (void) final; + +private: + int _transectCount (void) const; + void _rebuildCorridorPolygon(void); + + + QGCMapPolyline _corridorPolyline; + QList> _transectSegments; ///< Internal transect segments including grid exit, turnaround and internal camera points + + bool _ignoreRecalc; + int _entryPoint; + + QMap _metaDataMap; + SettingsFact _corridorWidthFact; + + static const char* _entryPointName; +}; diff --git a/src/MissionManager/CorridorScanComplexItemTest.cc b/src/MissionManager/CorridorScanComplexItemTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..bcbd9cf3fd56484e9bfa9f6517c98c333fe746b9 --- /dev/null +++ b/src/MissionManager/CorridorScanComplexItemTest.cc @@ -0,0 +1,157 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "CorridorScanComplexItemTest.h" +#include "QGCApplication.h" + +CorridorScanComplexItemTest::CorridorScanComplexItemTest(void) + : _offlineVehicle(NULL) +{ + _linePoints << QGeoCoordinate(47.633550640000003, -122.08982199) + << QGeoCoordinate(47.634129020000003, -122.08887249) + << QGeoCoordinate(47.633619320000001, -122.08811074); +} + +void CorridorScanComplexItemTest::init(void) +{ + UnitTest::init(); + + _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); + _corridorItem = new CorridorScanComplexItem(_offlineVehicle, this); +// _corridorItem->setTurnaroundDist(0); // Unit test written for no turnaround distance + _setPolyline(); + _corridorItem->setDirty(false); + + _rgCorridorPolygonSignals[corridorPolygonPathChangedIndex] = SIGNAL(pathChanged()); + + _multiSpyCorridorPolygon = new MultiSignalSpy(); + QCOMPARE(_multiSpyCorridorPolygon->init(_corridorItem->surveyAreaPolygon(), _rgCorridorPolygonSignals, _cCorridorPolygonSignals), true); +} + +void CorridorScanComplexItemTest::cleanup(void) +{ + delete _corridorItem; + delete _offlineVehicle; +} + +void CorridorScanComplexItemTest::_testDirty(void) +{ + Fact* fact = _corridorItem->corridorWidth(); + fact->setRawValue(fact->rawValue().toDouble() + 1); + QVERIFY(_corridorItem->dirty()); + _corridorItem->setDirty(false); + + changeFactValue(_corridorItem->cameraCalc()->distanceToSurface()); + QVERIFY(_corridorItem->dirty()); + _corridorItem->setDirty(false); + + QGeoCoordinate coord = _corridorItem->corridorPolyline()->vertexCoordinate(0); + coord.setLatitude(coord.latitude() + 1); + _corridorItem->corridorPolyline()->adjustVertex(1, coord); + QVERIFY(_corridorItem->dirty()); + _corridorItem->setDirty(false); +} + +void CorridorScanComplexItemTest::_testCameraTrigger(void) +{ +#if 0 + QCOMPARE(_corridorItem->property("cameraTrigger").toBool(), true); + + // Set up a grid + + for (int i=0; i<3; i++) { + _mapPolyline->appendVertex(_linePoints[i]); + } + + _corridorItem->setDirty(false); + _multiSpy->clearAllSignals(); + + int lastSeq = _corridorItem->lastSequenceNumber(); + QVERIFY(lastSeq > 0); + + // Turning off camera triggering should remove two camera trigger mission items, this should trigger: + // lastSequenceNumberChanged + // dirtyChanged + + _corridorItem->setProperty("cameraTrigger", false); + QVERIFY(_multiSpy->checkOnlySignalByMask(lastSequenceNumberChangedMask | dirtyChangedMask | cameraTriggerChangedMask)); + QCOMPARE(_multiSpy->pullIntFromSignalIndex(lastSequenceNumberChangedIndex), lastSeq - 2); + + _corridorItem->setDirty(false); + _multiSpy->clearAllSignals(); + + // Turn on camera triggering and make sure things go back to previous count + + _corridorItem->setProperty("cameraTrigger", true); + QVERIFY(_multiSpy->checkOnlySignalByMask(lastSequenceNumberChangedMask | dirtyChangedMask | cameraTriggerChangedMask)); + QCOMPARE(_multiSpy->pullIntFromSignalIndex(lastSequenceNumberChangedIndex), lastSeq); +#endif +} + +void CorridorScanComplexItemTest::_setPolyline(void) +{ + for (int i=0; i<_linePoints.count(); i++) { + QGeoCoordinate& vertex = _linePoints[i]; + _corridorItem->corridorPolyline()->appendVertex(vertex); + } +} + +#if 0 +void CorridorScanComplexItemTest::_testEntryLocation(void) +{ + _setPolygon(); + + for (double gridAngle=-360.0; gridAngle<=360.0; gridAngle++) { + _corridorItem->gridAngle()->setRawValue(gridAngle); + + QList rgSeenEntryCoords; + QList rgEntryLocation; + rgEntryLocation << SurveyMissionItem::EntryLocationTopLeft + << SurveyMissionItem::EntryLocationTopRight + << SurveyMissionItem::EntryLocationBottomLeft + << SurveyMissionItem::EntryLocationBottomRight; + + // Validate that each entry location is unique + for (int i=0; igridEntryLocation()->setRawValue(entryLocation); + QVERIFY(!rgSeenEntryCoords.contains(_corridorItem->coordinate())); + rgSeenEntryCoords << _corridorItem->coordinate(); + } + rgSeenEntryCoords.clear(); + } +} +#endif + +void CorridorScanComplexItemTest::_testItemCount(void) +{ + QList items; + + _corridorItem->turnAroundDistance()->setRawValue(20); + + _corridorItem->cameraTriggerInTurnAround()->setRawValue(false); + _corridorItem->appendMissionItems(items, this); + QCOMPARE(items.count() - 1, _corridorItem->lastSequenceNumber()); + items.clear(); + + _corridorItem->cameraTriggerInTurnAround()->setRawValue(true); + _corridorItem->appendMissionItems(items, this); + QCOMPARE(items.count() - 1, _corridorItem->lastSequenceNumber()); + items.clear(); +} + +void CorridorScanComplexItemTest::_testPathChanges(void) +{ + QGeoCoordinate vertex = _corridorItem->corridorPolyline()->vertexCoordinate(1); + vertex.setLatitude(vertex.latitude() + 0.01); + _corridorItem->corridorPolyline()->adjustVertex(1, vertex); + + QVERIFY(_multiSpyCorridorPolygon->checkSignalsByMask(corridorPolygonPathChangedMask)); +} diff --git a/src/MissionManager/CorridorScanComplexItemTest.h b/src/MissionManager/CorridorScanComplexItemTest.h new file mode 100644 index 0000000000000000000000000000000000000000..2f793843801054b69263de9965d0f647dc05d358 --- /dev/null +++ b/src/MissionManager/CorridorScanComplexItemTest.h @@ -0,0 +1,56 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "UnitTest.h" +#include "TCPLink.h" +#include "MultiSignalSpy.h" +#include "CorridorScanComplexItem.h" + +#include + +class CorridorScanComplexItemTest : public UnitTest +{ + Q_OBJECT + +public: + CorridorScanComplexItemTest(void); + +protected: + void init(void) final; + void cleanup(void) final; + +private slots: + void _testDirty(void); + void _testCameraTrigger(void); +// void _testEntryLocation(void); + void _testItemCount(void); + void _testPathChanges(void); + +private: + void _setPolyline(void); + + enum { + corridorPolygonPathChangedIndex = 0, + maxCorridorPolygonSignalIndex + }; + + enum { + corridorPolygonPathChangedMask = 1 << corridorPolygonPathChangedIndex, + }; + + static const size_t _cCorridorPolygonSignals = maxCorridorPolygonSignalIndex; + const char* _rgCorridorPolygonSignals[_cCorridorPolygonSignals]; + + Vehicle* _offlineVehicle; + MultiSignalSpy* _multiSpyCorridorPolygon; + CorridorScanComplexItem* _corridorItem; + QList _linePoints; +}; diff --git a/src/MissionManager/FWLandingPattern.FactMetaData.json b/src/MissionManager/FWLandingPattern.FactMetaData.json index 8d5a21f0022332061542c2e67f99cfa00ef6cc7d..ca77c066c2ef877f4e729492601a63872e09bd9b 100644 --- a/src/MissionManager/FWLandingPattern.FactMetaData.json +++ b/src/MissionManager/FWLandingPattern.FactMetaData.json @@ -1,6 +1,6 @@ [ { - "name": "Landing dist", + "name": "LandingDistance", "shortDescription": "Distance between landing and loiter points.", "type": "double", "units": "m", @@ -9,7 +9,7 @@ "defaultValue": 300.0 }, { - "name": "Landing heading", + "name": "LandingHeading", "shortDescription": "Heading from loiter point to land point.", "type": "double", "units": "deg", @@ -19,7 +19,7 @@ "defaultValue": 270.0 }, { - "name": "Loiter altitude", + "name": "LoiterAltitude", "shortDescription": "Aircraft will proceed to the loiter point and loiter until it reaches this altitude. Once altitude is reached the aircraft will proceed to land.", "type": "double", "units": "m", @@ -27,7 +27,7 @@ "defaultValue": 40.0 }, { - "name": "Loiter radius", + "name": "LoiterRadius", "shortDescription": "Loiter radius.", "type": "double", "decimalPlaces": 1, @@ -36,7 +36,7 @@ "defaultValue": 75.0 }, { - "name": "Landing altitude", + "name": "LandingAltitude", "shortDescription": "Altitude for landing point.", "type": "double", "units": "m", @@ -44,7 +44,7 @@ "defaultValue": 0.0 }, { - "name": "Descent rate", + "name": "DescentRate", "shortDescription": "Descent rate between landing and loiter altitude.", "type": "double", "units": "%", diff --git a/src/MissionManager/FixedWingLandingComplexItem.cc b/src/MissionManager/FixedWingLandingComplexItem.cc index 03e69b8e9932ea6d12b277a664a4cf5797d9d152..e81cb512abc96865179a319bcc15cbdc32363326 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.cc +++ b/src/MissionManager/FixedWingLandingComplexItem.cc @@ -20,12 +20,12 @@ QGC_LOGGING_CATEGORY(FixedWingLandingComplexItemLog, "FixedWingLandingComplexIte const char* FixedWingLandingComplexItem::jsonComplexItemTypeValue = "fwLandingPattern"; -const char* FixedWingLandingComplexItem::_loiterToLandDistanceName = "Landing dist"; -const char* FixedWingLandingComplexItem::_landingHeadingName = "Landing heading"; -const char* FixedWingLandingComplexItem::_loiterAltitudeName = "Loiter altitude"; -const char* FixedWingLandingComplexItem::_loiterRadiusName = "Loiter radius"; -const char* FixedWingLandingComplexItem::_landingAltitudeName = "Landing altitude"; -const char* FixedWingLandingComplexItem::_fallRateName = "Descent rate"; +const char* FixedWingLandingComplexItem::loiterToLandDistanceName = "LandingDistance"; +const char* FixedWingLandingComplexItem::landingHeadingName = "LandingHeading"; +const char* FixedWingLandingComplexItem::loiterAltitudeName = "LoiterAltitude"; +const char* FixedWingLandingComplexItem::loiterRadiusName = "LoiterRadius"; +const char* FixedWingLandingComplexItem::landingAltitudeName = "LandingAltitude"; +const char* FixedWingLandingComplexItem::fallRateName = "DescentRate"; const char* FixedWingLandingComplexItem::_jsonLoiterCoordinateKey = "loiterCoordinate"; const char* FixedWingLandingComplexItem::_jsonLoiterRadiusKey = "loiterRadius"; @@ -35,44 +35,25 @@ const char* FixedWingLandingComplexItem::_jsonLandingCoordinateKey = "lan const char* FixedWingLandingComplexItem::_jsonLandingAltitudeRelativeKey = "landAltitudeRelative"; const char* FixedWingLandingComplexItem::_jsonFallRateKey = "fallRate"; -QMap FixedWingLandingComplexItem::_metaDataMap; - FixedWingLandingComplexItem::FixedWingLandingComplexItem(Vehicle* vehicle, QObject* parent) - : ComplexMissionItem(vehicle, parent) - , _sequenceNumber(0) - , _dirty(false) - , _landingCoordSet(false) - , _ignoreRecalcSignals(false) - , _landingDistanceFact (0, _loiterToLandDistanceName, FactMetaData::valueTypeDouble) - , _loiterAltitudeFact (0, _loiterAltitudeName, FactMetaData::valueTypeDouble) - , _loiterRadiusFact (0, _loiterRadiusName, FactMetaData::valueTypeDouble) - , _landingHeadingFact (0, _landingHeadingName, FactMetaData::valueTypeDouble) - , _landingAltitudeFact (0, _landingAltitudeName, FactMetaData::valueTypeDouble) - , _fallRateFact (0, _fallRateName, FactMetaData::valueTypeDouble) - , _loiterClockwise(true) - , _loiterAltitudeRelative(true) - , _landingAltitudeRelative(true) + : ComplexMissionItem (vehicle, parent) + , _sequenceNumber (0) + , _dirty (false) + , _landingCoordSet (false) + , _ignoreRecalcSignals (false) + , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/FWLandingPattern.FactMetaData.json"), this)) + , _landingDistanceFact (_metaDataMap[loiterToLandDistanceName]) + , _loiterAltitudeFact (_metaDataMap[loiterAltitudeName]) + , _loiterRadiusFact (_metaDataMap[loiterRadiusName]) + , _landingHeadingFact (_metaDataMap[landingHeadingName]) + , _landingAltitudeFact (_metaDataMap[landingAltitudeName]) + , _fallRateFact (_metaDataMap[fallRateName]) + , _loiterClockwise (true) + , _loiterAltitudeRelative (true) + , _landingAltitudeRelative (true) { _editorQml = "qrc:/qml/FWLandingPatternEditor.qml"; - if (_metaDataMap.isEmpty()) { - _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/FWLandingPattern.FactMetaData.json"), NULL /* metaDataParent */); - } - - _landingDistanceFact.setMetaData (_metaDataMap[_loiterToLandDistanceName]); - _loiterAltitudeFact.setMetaData (_metaDataMap[_loiterAltitudeName]); - _loiterRadiusFact.setMetaData (_metaDataMap[_loiterRadiusName]); - _landingHeadingFact.setMetaData (_metaDataMap[_landingHeadingName]); - _landingAltitudeFact.setMetaData (_metaDataMap[_landingAltitudeName]); - _fallRateFact.setMetaData (_metaDataMap[_fallRateName]); - - _landingDistanceFact.setRawValue (_landingDistanceFact.rawDefaultValue()); - _loiterAltitudeFact.setRawValue (_loiterAltitudeFact.rawDefaultValue()); - _loiterRadiusFact.setRawValue (_loiterRadiusFact.rawDefaultValue()); - _landingHeadingFact.setRawValue (_landingHeadingFact.rawDefaultValue()); - _landingAltitudeFact.setRawValue (_landingAltitudeFact.rawDefaultValue()); - _fallRateFact.setRawValue (_fallRateFact.rawDefaultValue()); - connect(&_loiterAltitudeFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_updateLoiterCoodinateAltitudeFromFact); connect(&_landingAltitudeFact, &Fact::valueChanged, this, &FixedWingLandingComplexItem::_updateLandingCoodinateAltitudeFromFact); diff --git a/src/MissionManager/FixedWingLandingComplexItem.h b/src/MissionManager/FixedWingLandingComplexItem.h index 0c838076cb40793b2dfb77ee595c525b1c829614..9d04c053af843586181049b071bdd2317d2d71e8 100644 --- a/src/MissionManager/FixedWingLandingComplexItem.h +++ b/src/MissionManager/FixedWingLandingComplexItem.h @@ -77,6 +77,7 @@ public: int sequenceNumber (void) const final { return _sequenceNumber; } double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } void appendMissionItems (QList& items, QObject* missionItemParent) final; void applyNewAltitude (double newAltitude) final; @@ -91,6 +92,13 @@ public: static const char* jsonComplexItemTypeValue; + static const char* loiterToLandDistanceName; + static const char* loiterAltitudeName; + static const char* loiterRadiusName; + static const char* landingHeadingName; + static const char* landingAltitudeName; + static const char* fallRateName; + signals: void loiterCoordinateChanged (QGeoCoordinate coordinate); void loiterTangentCoordinateChanged (QGeoCoordinate coordinate); @@ -121,6 +129,8 @@ private: bool _landingCoordSet; bool _ignoreRecalcSignals; + QMap _metaDataMap; + Fact _landingDistanceFact; Fact _loiterAltitudeFact; Fact _loiterRadiusFact; @@ -132,15 +142,6 @@ private: bool _loiterAltitudeRelative; bool _landingAltitudeRelative; - static QMap _metaDataMap; - - static const char* _loiterToLandDistanceName; - static const char* _loiterAltitudeName; - static const char* _loiterRadiusName; - static const char* _landingHeadingName; - static const char* _landingAltitudeName; - static const char* _fallRateName; - static const char* _jsonLoiterCoordinateKey; static const char* _jsonLoiterRadiusKey; static const char* _jsonLoiterClockwiseKey; diff --git a/src/MissionManager/GeoFenceManager.cc b/src/MissionManager/GeoFenceManager.cc index 448be9eb03b4d79462a1eec083b950d2a7e40f46..ce6ce1933efecfbc1ef788047a598f12afcf245e 100644 --- a/src/MissionManager/GeoFenceManager.cc +++ b/src/MissionManager/GeoFenceManager.cc @@ -99,11 +99,8 @@ void GeoFenceManager::sendToVehicle(const QGeoCoordinate& breachReturn, fenceItems.append(item); } + // Plan manager takes control of MissionItems, so no need to delete _planManager.writeMissionItems(fenceItems); - - for (int i=0; ideleteLater(); - } } void GeoFenceManager::removeAll(void) diff --git a/src/MissionManager/MavCmdInfoCommon.json b/src/MissionManager/MavCmdInfoCommon.json index ff0a85c3120509505bbfd99e48a3cd88be0e617c..9a7dd6cbcd962409dda9e1d84456787cdde3d03a 100644 --- a/src/MissionManager/MavCmdInfoCommon.json +++ b/src/MissionManager/MavCmdInfoCommon.json @@ -237,33 +237,6 @@ }, { "id": 32, "rawName": "MAV_CMD_DO_FOLLOW", "friendlyName": "Follow Me" }, { "id": 33, "rawName": "MAV_CMD_DO_FOLLOW_REPOSITION", "friendlyName": "Vehicle reposition" }, - { - "id": 80, - "rawName": "MAV_CMD_NAV_ROI", - "friendlyName": "Region of interest (nav)", - "description": "Sets the region of interest for cameras.", - "specifiesCoordinate": true, - "standaloneCoordinate": true, - "friendlyEdit": true, - "category": "Camera", - "param1": { - "label": "Mode", - "enumStrings": "None,Next waypoint,Mission item,Location,ROI item", - "enumValues": "0,1,2,3,4", - "default": 3, - "decimalPlaces": 0 - }, - "param2": { - "label": "Mission Index", - "default": 0, - "decimalPlaces": 0 - }, - "param3": { - "label": "ROI Index", - "default": 0, - "decimalPlaces": 0 - } - }, { "id": 81, "rawName": "MAV_CMD_NAV_PATHPLANNING", @@ -651,11 +624,57 @@ "default": 0 } }, + { + "id": 195, + "rawName": "MAV_CMD_DO_SET_ROI_LOCATION", + "friendlyName": "Region of interest (ROI)" , + "description": "Sets the region of interest for cameras.", + "specifiesCoordinate": true, + "standaloneCoordinate": true, + "friendlyEdit": true, + "category": "Camera" + }, + { + "id": 196, + "rawName": "MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET", + "friendlyName": "ROI to next waypoint" , + "description": "Sets the region of interest to point towards the next waypoint with optional offsets.", + "specifiesCoordinate": false, + "friendlyEdit": true, + "category": "Camera", + "param5": { + "label": "Pitch offset", + "default": 0, + "units": "deg", + "decimalPlaces": 0 + }, + "param6": { + "label": "Roll offset", + "default": 0, + "units": "deg", + "decimalPlaces": 0 + }, + "param7": { + "label": "Yaw offset", + "default": 0, + "units": "deg", + "decimalPlaces": 0 + } + }, + { + "id": 197, + "rawName": "MAV_CMD_DO_SET_ROI_NONE", + "friendlyName": "Cancel ROI" , + "description": "Cancels the region of interest.", + "specifiesCoordinate": false, + "friendlyEdit": true, + "category": "Camera" + }, { "id": 200, "rawName": "MAV_CMD_DO_CONTROL_VIDEO", "friendlyName": "Control video" }, { "id": 201, "rawName": "MAV_CMD_DO_SET_ROI", - "friendlyName": "Region of interest (cmd)" , + "friendlyName": "Region of interest" , "description": "Sets the region of interest for cameras.", "specifiesCoordinate": true, "standaloneCoordinate": true, diff --git a/src/MissionManager/MissionCommandTreeTest.cc b/src/MissionManager/MissionCommandTreeTest.cc index bd16a41f5d9cd9595379deaa78a6b93f12a58947..9407d9f8b6c64d0480a8cede0e64dcd740c3a5e7 100644 --- a/src/MissionManager/MissionCommandTreeTest.cc +++ b/src/MissionManager/MissionCommandTreeTest.cc @@ -70,7 +70,9 @@ void MissionCommandTreeTest::_checkBaseValues(const MissionCommandUIInfo* uiInfo QCOMPARE(uiInfo->isStandaloneCoordinate(), true); QCOMPARE(uiInfo->specifiesCoordinate(), true); for (int i=1; i<=7; i++) { - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i); + bool showUI; + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI); + QVERIFY(showUI); QVERIFY(paramInfo); QCOMPARE(paramInfo->decimalPlaces(), 1); QCOMPARE(paramInfo->defaultValue(), 1.0); @@ -91,7 +93,9 @@ void MissionCommandTreeTest::_checkOverrideParamValues(const MissionCommandUIInf { QString overrideString = QString("override fw %1 %2").arg(command).arg(paramIndex); - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(paramIndex); + bool showUI; + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(paramIndex, showUI); + QVERIFY(showUI); QVERIFY(paramInfo); QCOMPARE(paramInfo->decimalPlaces(), 1); QCOMPARE(paramInfo->defaultValue(), 1.0); @@ -109,6 +113,7 @@ void MissionCommandTreeTest::_checkOverrideParamValues(const MissionCommandUIInf // Verifies that values match settings for an override void MissionCommandTreeTest::_checkOverrideValues(const MissionCommandUIInfo* uiInfo, int command) { + bool showUI; QString overrideString = QString("override fw %1").arg(command); QVERIFY(uiInfo != NULL); @@ -121,9 +126,12 @@ void MissionCommandTreeTest::_checkOverrideValues(const MissionCommandUIInfo* ui QCOMPARE(uiInfo->friendlyName(), _friendlyName(command)); QCOMPARE(uiInfo->isStandaloneCoordinate(), false); QCOMPARE(uiInfo->specifiesCoordinate(), false); - QVERIFY(uiInfo->getParamInfo(2) == NULL); - QVERIFY(uiInfo->getParamInfo(4) == NULL); - QVERIFY(uiInfo->getParamInfo(6) == NULL); + QVERIFY(uiInfo->getParamInfo(2, showUI)); + QCOMPARE(showUI, false); + QVERIFY(uiInfo->getParamInfo(4, showUI)); + QCOMPARE(showUI, false); + QVERIFY(uiInfo->getParamInfo(6, showUI)); + QCOMPARE(showUI, false); _checkOverrideParamValues(uiInfo, command, 1); _checkOverrideParamValues(uiInfo, command, 3); _checkOverrideParamValues(uiInfo, command, 5); @@ -131,6 +139,8 @@ void MissionCommandTreeTest::_checkOverrideValues(const MissionCommandUIInfo* ui void MissionCommandTreeTest::testJsonLoad(void) { + bool showUI; + // Test loading from the bad command list MissionCommandList* commandList = _commandTree->_staticCommandTree[MAV_AUTOPILOT_GENERIC][MAV_TYPE_GENERIC]; QVERIFY(commandList != NULL); @@ -148,14 +158,16 @@ void MissionCommandTreeTest::testJsonLoad(void) QCOMPARE(uiInfo->isStandaloneCoordinate(), false); QCOMPARE(uiInfo->specifiesCoordinate(), false); for (int i=1; i<=7; i++) { - QVERIFY(uiInfo->getParamInfo(i) == NULL); + QVERIFY(uiInfo->getParamInfo(i, showUI) == NULL); + QCOMPARE(showUI, false); } // Command 2 should all values defaulted for param 1 uiInfo = commandList->getUIInfo((MAV_CMD)2); QVERIFY(uiInfo != NULL); - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(1); + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(1, showUI); QVERIFY(paramInfo); + QCOMPARE(showUI, true); QCOMPARE(paramInfo->decimalPlaces(), -1); QCOMPARE(paramInfo->defaultValue(), 0.0); QCOMPARE(paramInfo->enumStrings().count(), 0); @@ -164,7 +176,8 @@ void MissionCommandTreeTest::testJsonLoad(void) QCOMPARE(paramInfo->param(), 1); QVERIFY(paramInfo->units().isEmpty()); for (int i=2; i<=7; i++) { - QVERIFY(uiInfo->getParamInfo(i) == NULL); + QVERIFY(uiInfo->getParamInfo(i, showUI) == NULL); + QCOMPARE(showUI, false); } // Command 3 should have all values set diff --git a/src/MissionManager/MissionCommandUIInfo.cc b/src/MissionManager/MissionCommandUIInfo.cc index ad63a6b1d77006ce0e45f9e87ea6ffe017f8e1ee..c12b7aaa8c94fa68adc02efa52f630dcafee747c 100644 --- a/src/MissionManager/MissionCommandUIInfo.cc +++ b/src/MissionManager/MissionCommandUIInfo.cc @@ -407,11 +407,15 @@ bool MissionCommandUIInfo::loadJsonInfo(const QJsonObject& jsonObject, bool requ return true; } -const MissionCmdParamInfo* MissionCommandUIInfo::getParamInfo(int index) const +const MissionCmdParamInfo* MissionCommandUIInfo::getParamInfo(int index, bool& showUI) const { - if (!_paramRemoveList.contains(index) && _paramInfoMap.contains(index)) { - return _paramInfoMap[index]; - } else { - return NULL; + const MissionCmdParamInfo* paramInfo = NULL; + + if (_paramInfoMap.contains(index)) { + paramInfo = _paramInfoMap[index]; } + + showUI = (paramInfo != NULL) && !_paramRemoveList.contains(index); + + return paramInfo; } diff --git a/src/MissionManager/MissionCommandUIInfo.h b/src/MissionManager/MissionCommandUIInfo.h index f2a66929308c4ec47cd8ff45aca2eaa8779c650e..f3c50a714bb57eb51f7c8393be53116d37094c24 100644 --- a/src/MissionManager/MissionCommandUIInfo.h +++ b/src/MissionManager/MissionCommandUIInfo.h @@ -138,8 +138,11 @@ public: /// @return true: success, false: failure, errorString set bool loadJsonInfo(const QJsonObject& jsonObject, bool requireFullObject, QString& errorString); - /// Return param info for index, NULL for param should not be shown - const MissionCmdParamInfo* getParamInfo(int index) const; + /// Retruns parameter information for specified parameter + /// @param index paremeter index to retrieve, 1-7 + /// @param showUI true: show parameter in editor, false: hide parameter in editor + /// @return Param info for index, NULL for none available + const MissionCmdParamInfo* getParamInfo(int index, bool& showUI) const; private: QString _loadErrorString(const QString& errorString) const; diff --git a/src/MissionManager/MissionController.cc b/src/MissionManager/MissionController.cc index 414286e407c7e4b4d6ff74817b0b2a20173a76ef..b093c3f58acc82e934cbb2104249a54ee7e7dcd9 100644 --- a/src/MissionManager/MissionController.cc +++ b/src/MissionManager/MissionController.cc @@ -19,7 +19,7 @@ #include "SurveyMissionItem.h" #include "FixedWingLandingComplexItem.h" #include "StructureScanComplexItem.h" -#include "StructureScanComplexItem.h" +#include "CorridorScanComplexItem.h" #include "JsonHelper.h" #include "ParameterManager.h" #include "QGroundControlQmlGlobal.h" @@ -63,6 +63,7 @@ MissionController::MissionController(PlanMasterController* masterController, QOb , _surveyMissionItemName(tr("Survey")) , _fwLandingMissionItemName(tr("Fixed Wing Landing")) , _structureScanMissionItemName(tr("Structure Scan")) + , _corridorScanMissionItemName(tr("Corridor Scan")) , _appSettings(qgcApp()->toolbox()->settingsManager()->appSettings()) , _progressPct(0) , _currentPlanViewIndex(-1) @@ -91,6 +92,7 @@ void MissionController::_resetMissionFlightStatus(void) _missionFlightStatus.vehicleSpeed = _controllerVehicle->multiRotor() || _managerVehicle->vtol() ? _missionFlightStatus.hoverSpeed : _missionFlightStatus.cruiseSpeed; _missionFlightStatus.vehicleYaw = 0.0; _missionFlightStatus.gimbalYaw = std::numeric_limits::quiet_NaN(); + _missionFlightStatus.gimbalPitch = std::numeric_limits::quiet_NaN(); // Battery information @@ -174,7 +176,7 @@ void MissionController::_newMissionItemsAvailableFromVehicle(bool removeAllReque for (; iappend(new SimpleMissionItem(_controllerVehicle, *missionItem, this)); + newControllerMissionItems->append(new SimpleMissionItem(_controllerVehicle, _editMode, *missionItem, this)); } _deinitAllVisualItems(); @@ -188,9 +190,7 @@ void MissionController::_newMissionItemsAvailableFromVehicle(bool removeAllReque _addMissionSettings(_visualItems, _editMode && _visualItems->count() > 0 /* addToCenter */); } - if (_editMode) { - MissionController::_scanForAdditionalSettings(_visualItems, _controllerVehicle); - } + MissionController::_scanForAdditionalSettings(_visualItems, _controllerVehicle); _initAllVisualItems(); _updateContainsItems(); @@ -289,14 +289,14 @@ void MissionController::convertToKMLDocument(QDomDocument& document) continue; } const MissionCommandUIInfo* uiInfo = \ - qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_controllerVehicle, item->command()); + qgcApp()->toolbox()->missionCommandTree()->getUIInfo(_controllerVehicle, item->command()); if (uiInfo && uiInfo->specifiesCoordinate() && !uiInfo->isStandaloneCoordinate()) { coord = QString::number(item->param6(),'f',7) \ - + "," \ - + QString::number(item->param5(),'f',7) \ - + "," \ - + QString::number(item->param7() + altitude,'f',2); + + "," \ + + QString::number(item->param5(),'f',7) \ + + "," \ + + QString::number(item->param7() + altitude,'f',2); coords.append(coord); } } @@ -312,11 +312,8 @@ void MissionController::sendItemsToVehicle(Vehicle* vehicle, QmlObjectListModel* _convertToMissionItems(visualMissionItems, rgMissionItems, vehicle); + // PlanManager takes control of MissionItems so no need to delete vehicle->missionManager()->writeMissionItems(rgMissionItems); - - for (int i=0; ideleteLater(); - } } } @@ -340,7 +337,10 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) newItem->setCommand(MavlinkQmlSingleton::MAV_CMD_NAV_WAYPOINT); _initVisualItem(newItem); if (_visualItems->count() == 1) { - newItem->setCommand(_controllerVehicle->vtol() ? MavlinkQmlSingleton::MAV_CMD_NAV_VTOL_TAKEOFF : MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF); + MavlinkQmlSingleton::Qml_MAV_CMD takeoffCmd = _controllerVehicle->vtol() ? MavlinkQmlSingleton::MAV_CMD_NAV_VTOL_TAKEOFF : MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF; + if (_controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains((MAV_CMD)takeoffCmd)) { + newItem->setCommand(takeoffCmd); + } } newItem->setDefaultsForCommand(); if ((MAV_CMD)newItem->command() == MAV_CMD_NAV_WAYPOINT) { @@ -352,6 +352,33 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) newItem->missionItem().setParam7(prevAltitude); } } + newItem->setMissionFlightStatus(_missionFlightStatus); + _visualItems->insert(i, newItem); + + _recalcAll(); + + return newItem->sequenceNumber(); +} + +int MissionController::insertROIMissionItem(QGeoCoordinate coordinate, int i) +{ + int sequenceNumber = _nextSequenceNumber(); + SimpleMissionItem * newItem = new SimpleMissionItem(_controllerVehicle, this); + newItem->setSequenceNumber(sequenceNumber); + newItem->setCommand((MavlinkQmlSingleton::Qml_MAV_CMD)(_controllerVehicle->firmwarePlugin()->supportedMissionCommands().contains((MAV_CMD)MAV_CMD_DO_SET_ROI_LOCATION) ? + MAV_CMD_DO_SET_ROI_LOCATION : + MAV_CMD_DO_SET_ROI)); + _initVisualItem(newItem); + newItem->setDefaultsForCommand(); + newItem->setCoordinate(coordinate); + + double prevAltitude; + MAV_FRAME prevFrame; + + if (_findPreviousAltitude(i, &prevAltitude, &prevFrame)) { + newItem->missionItem().setFrame(prevFrame); + newItem->missionItem().setParam7(prevAltitude); + } _visualItems->insert(i, newItem); _recalcAll(); @@ -362,22 +389,41 @@ int MissionController::insertSimpleMissionItem(QGeoCoordinate coordinate, int i) int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate mapCenterCoordinate, int i) { ComplexMissionItem* newItem; + bool surveyStyleItem = false; int sequenceNumber = _nextSequenceNumber(); if (itemName == _surveyMissionItemName) { newItem = new SurveyMissionItem(_controllerVehicle, _visualItems); newItem->setCoordinate(mapCenterCoordinate); - // If the vehicle is known to have a gimbal then we automatically point the gimbal straight down if not already set + surveyStyleItem = true; + } else if (itemName == _fwLandingMissionItemName) { + newItem = new FixedWingLandingComplexItem(_controllerVehicle, _visualItems); + } else if (itemName == _structureScanMissionItemName) { + newItem = new StructureScanComplexItem(_controllerVehicle, _visualItems); + } else if (itemName == _corridorScanMissionItemName) { + newItem = new CorridorScanComplexItem(_controllerVehicle, _visualItems); + surveyStyleItem = true; + } else { + qWarning() << "Internal error: Unknown complex item:" << itemName; + return sequenceNumber; + } + + if (surveyStyleItem) { bool rollSupported = false; bool pitchSupported = false; bool yawSupported = false; + + // If the vehicle is known to have a gimbal then we automatically point the gimbal straight down if not already set + MissionSettingsItem* settingsItem = _visualItems->value(0); CameraSection* cameraSection = settingsItem->cameraSection(); + // Set camera to photo mode (leave alone if user already specified) if (cameraSection->cameraModeSupported() && !cameraSection->specifyCameraMode()) { cameraSection->setSpecifyCameraMode(true); cameraSection->cameraMode()->setRawValue(CAMERA_MODE_IMAGE_SURVEY); } + // Point gimbal straight down if (_controllerVehicle->firmwarePlugin()->hasGimbal(_controllerVehicle, rollSupported, pitchSupported, yawSupported) && pitchSupported) { // If the user already specified a gimbal angle leave it alone @@ -386,14 +432,8 @@ int MissionController::insertComplexMissionItem(QString itemName, QGeoCoordinate cameraSection->gimbalPitch()->setRawValue(-90.0); } } - } else if (itemName == _fwLandingMissionItemName) { - newItem = new FixedWingLandingComplexItem(_controllerVehicle, _visualItems); - } else if (itemName == _structureScanMissionItemName) { - newItem = new StructureScanComplexItem(_controllerVehicle, _visualItems); - } else { - qWarning() << "Internal error: Unknown complex item:" << itemName; - return sequenceNumber; } + newItem->setSequenceNumber(sequenceNumber); _initVisualItem(newItem); @@ -411,17 +451,17 @@ void MissionController::removeMissionItem(int index) return; } - bool surveyRemoved = _visualItems->value(index); + bool removeSurveyStyle = _visualItems->value(index) || _visualItems->value(index); VisualMissionItem* item = qobject_cast(_visualItems->removeAt(index)); _deinitVisualItem(item); item->deleteLater(); - if (surveyRemoved) { - // Determine if the mission still has another survey in it + if (removeSurveyStyle) { + // Determine if the mission still has another survey style item in it bool foundSurvey = false; for (int i=1; i<_visualItems->count(); i++) { - if (_visualItems->value(i)) { + if (_visualItems->value(i) || _visualItems->value(index)) { foundSurvey = true; break; } @@ -453,6 +493,7 @@ void MissionController::removeAll(void) { if (_visualItems) { _deinitAllVisualItems(); + _visualItems->clearAndDeleteContents(); _visualItems->deleteLater(); _settingsItem = NULL; _visualItems = new QmlObjectListModel(this); @@ -671,6 +712,15 @@ bool MissionController::_loadJsonMissionFileV2(const QJsonObject& json, QmlObjec nextSequenceNumber = structureItem->lastSequenceNumber() + 1; qCDebug(MissionControllerLog) << "Structure Scan load complete: nextSequenceNumber" << nextSequenceNumber; visualItems->append(structureItem); + } else if (complexItemType == CorridorScanComplexItem::jsonComplexItemTypeValue) { + qCDebug(MissionControllerLog) << "Loading Corridor Scan: nextSequenceNumber" << nextSequenceNumber; + CorridorScanComplexItem* corridorItem = new CorridorScanComplexItem(_controllerVehicle, visualItems); + if (!corridorItem->load(itemObject, nextSequenceNumber++, errorString)) { + return false; + } + nextSequenceNumber = corridorItem->lastSequenceNumber() + 1; + qCDebug(MissionControllerLog) << "Corridor Scan load complete: nextSequenceNumber" << nextSequenceNumber; + visualItems->append(corridorItem); } else if (complexItemType == MissionSettingsItem::jsonComplexItemTypeValue) { qCDebug(MissionControllerLog) << "Loading Mission Settings: nextSequenceNumber" << nextSequenceNumber; MissionSettingsItem* settingsItem = new MissionSettingsItem(_controllerVehicle, visualItems); @@ -985,13 +1035,14 @@ void MissionController::_recalcWaypointLines(void) bool firstCoordinateItem = true; VisualMissionItem* lastCoordinateItem = qobject_cast(_visualItems->get(0)); - bool showHomePosition = _settingsItem->coordinate().isValid(); + bool homePositionValid = _settingsItem->coordinate().isValid(); - qCDebug(MissionControllerLog) << "_recalcWaypointLines showHomePosition" << showHomePosition; + qCDebug(MissionControllerLog) << "_recalcWaypointLines homePositionValid" << homePositionValid; CoordVectHashTable old_table = _linesTable; _linesTable.clear(); _waypointLines.clear(); + _waypointPath.clear(); bool linkEndToHome; SimpleMissionItem* lastItem = _visualItems->value(_visualItems->count() - 1); @@ -1005,29 +1056,39 @@ void MissionController::_recalcWaypointLines(void) for (int i=1; i<_visualItems->count(); i++) { VisualMissionItem* item = qobject_cast(_visualItems->get(i)); - - // If we still haven't found the first coordinate item and we hit a takeoff command, link back to home - if (firstCoordinateItem && - item->isSimpleItem() && - (qobject_cast(item)->command() == MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF || - qobject_cast(item)->command() == MavlinkQmlSingleton::MAV_CMD_NAV_VTOL_TAKEOFF)) { - linkStartToHome = true; + // If we still haven't found the first coordinate item and we hit a takeoff command this means the mission starts from the ground. + // Link the first item back to home to show that. + if (firstCoordinateItem && item->isSimpleItem()) { + MAV_CMD command = (MAV_CMD)qobject_cast(item)->command(); + if (command == MAV_CMD_NAV_TAKEOFF || command == MAV_CMD_NAV_VTOL_TAKEOFF) { + linkStartToHome = true; + } } - if (item->specifiesCoordinate()) { - if (!item->isStandaloneCoordinate()) { - firstCoordinateItem = false; - VisualItemPair pair(lastCoordinateItem, item); - if (lastCoordinateItem != _settingsItem || (showHomePosition && linkStartToHome)) { + if (item->specifiesCoordinate() && !item->isStandaloneCoordinate()) { + firstCoordinateItem = false; + if (lastCoordinateItem != _settingsItem || (homePositionValid && linkStartToHome)) { + if (_editMode) { + VisualItemPair pair(lastCoordinateItem, item); _addWaypointLineSegment(old_table, pair); } - lastCoordinateItem = item; } + _waypointPath.append(QVariant::fromValue(item->coordinate())); + lastCoordinateItem = item; } } - if (linkEndToHome && lastCoordinateItem != _settingsItem && showHomePosition) { - VisualItemPair pair(lastCoordinateItem, _settingsItem); - _addWaypointLineSegment(old_table, pair); + + if (linkStartToHome && homePositionValid) { + _waypointPath.prepend(QVariant::fromValue(_settingsItem->coordinate())); + } + + if (linkEndToHome && lastCoordinateItem != _settingsItem && homePositionValid) { + if (_editMode) { + VisualItemPair pair(lastCoordinateItem, _settingsItem); + _addWaypointLineSegment(old_table, pair); + } else { + _waypointPath.append(QVariant::fromValue(_settingsItem->coordinate())); + } } { @@ -1047,7 +1108,16 @@ void MissionController::_recalcWaypointLines(void) _recalcMissionFlightStatus(); + if (_waypointPath.count() == 0) { + // MapPolyLine has a bug where if you can from a path which has elements to an empty path the line drawn + // is not cleared from the map. This hack works around that since it causes the previous lines to be remove + // as then doesn't draw anything on the map. + _waypointPath.append(QVariant::fromValue(QGeoCoordinate(0, 0))); + _waypointPath.append(QVariant::fromValue(QGeoCoordinate(0, 0))); + } + emit waypointLinesChanged(); + emit waypointPathChanged(); } void MissionController::_updateBatteryInfo(int waypointIndex) @@ -1056,6 +1126,9 @@ void MissionController::_updateBatteryInfo(int waypointIndex) _missionFlightStatus.hoverAmpsTotal = (_missionFlightStatus.hoverTime / 60.0) * _missionFlightStatus.hoverAmps; _missionFlightStatus.cruiseAmpsTotal = (_missionFlightStatus.cruiseTime / 60.0) * _missionFlightStatus.cruiseAmps; _missionFlightStatus.batteriesRequired = ceil((_missionFlightStatus.hoverAmpsTotal + _missionFlightStatus.cruiseAmpsTotal) / _missionFlightStatus.ampMinutesAvailable); + // FIXME: Battery change point code pretty much doesn't work. The reason is that is treats complex items as a black box. It needs to be able to look + // inside complex items in order to determine a swap point that is interior to a complex item. Current the swap point display in PlanToolbar is + // disabled to do this problem. if (waypointIndex != -1 && _missionFlightStatus.batteriesRequired == 2 && _missionFlightStatus.batteryChangePoint == -1) { _missionFlightStatus.batteryChangePoint = waypointIndex - 1; } @@ -1196,12 +1269,13 @@ void MissionController::_recalcMissionFlightStatus() } // Look for gimbal change - if (_managerVehicle->vehicleYawsToNextWaypointInMission()) { - // We current only support gimbal display in this mode - double gimbalYaw = item->specifiedGimbalYaw(); - if (!qIsNaN(gimbalYaw)) { - _missionFlightStatus.gimbalYaw = gimbalYaw; - } + double gimbalYaw = item->specifiedGimbalYaw(); + if (!qIsNaN(gimbalYaw)) { + _missionFlightStatus.gimbalYaw = gimbalYaw; + } + double gimbalPitch = item->specifiedGimbalPitch(); + if (!qIsNaN(gimbalPitch)) { + _missionFlightStatus.gimbalPitch = gimbalPitch; } if (i == 0) { @@ -1480,6 +1554,7 @@ void MissionController::_initVisualItem(VisualMissionItem* visualItem) connect(visualItem, &VisualMissionItem::exitCoordinateHasRelativeAltitudeChanged, this, &MissionController::_recalcWaypointLines); connect(visualItem, &VisualMissionItem::specifiedFlightSpeedChanged, this, &MissionController::_recalcMissionFlightStatus); connect(visualItem, &VisualMissionItem::specifiedGimbalYawChanged, this, &MissionController::_recalcMissionFlightStatus); + connect(visualItem, &VisualMissionItem::specifiedGimbalPitchChanged, this, &MissionController::_recalcMissionFlightStatus); connect(visualItem, &VisualMissionItem::terrainAltitudeChanged, this, &MissionController::_recalcMissionFlightStatus); connect(visualItem, &VisualMissionItem::lastSequenceNumberChanged, this, &MissionController::_recalcSequence); @@ -1732,8 +1807,10 @@ void MissionController::setDirty(bool dirty) void MissionController::_scanForAdditionalSettings(QmlObjectListModel* visualItems, Vehicle* vehicle) { - // First we look for a Fixed Wing Landing Pattern which is at the end - FixedWingLandingComplexItem::scanForItem(visualItems, vehicle); + if (_editMode) { + // First we look for a Fixed Wing Landing Pattern which is at the end + FixedWingLandingComplexItem::scanForItem(visualItems, vehicle); + } int scanIndex = 0; while (scanIndex < visualItems->count()) { @@ -1741,11 +1818,13 @@ void MissionController::_scanForAdditionalSettings(QmlObjectListModel* visualIte qCDebug(MissionControllerLog) << "MissionController::_scanForAdditionalSettings count:scanIndex" << visualItems->count() << scanIndex; - MissionSettingsItem* settingsItem = qobject_cast(visualItem); - if (settingsItem) { - scanIndex++; - settingsItem->scanForMissionSettings(visualItems, scanIndex); - continue; + if (_editMode) { + MissionSettingsItem* settingsItem = qobject_cast(visualItem); + if (settingsItem) { + scanIndex++; + settingsItem->scanForMissionSettings(visualItems, scanIndex); + continue; + } } SimpleMissionItem* simpleItem = qobject_cast(visualItem); @@ -1786,6 +1865,7 @@ QStringList MissionController::complexMissionItemNames(void) const QStringList complexItems; complexItems.append(_surveyMissionItemName); + complexItems.append(_corridorScanMissionItemName); if (_controllerVehicle->fixedWing()) { complexItems.append(_fwLandingMissionItemName); } diff --git a/src/MissionManager/MissionController.h b/src/MissionManager/MissionController.h index 4e2b12624992d6db1f50542b551fcfd3a4c6702b..390a4e8a62be8a3304959eea4d2168e90151068f 100644 --- a/src/MissionManager/MissionController.h +++ b/src/MissionManager/MissionController.h @@ -54,6 +54,7 @@ public: double vehicleSpeed; ///< Either cruise or hover speed based on vehicle type and vtol state double vehicleYaw; double gimbalYaw; ///< NaN signals yaw was never changed + double gimbalPitch; ///< NaN signals pitch was never changed int mAhBattery; ///< 0 for not available double hoverAmps; ///< Amp consumption during hover double cruiseAmps; ///< Amp consumption during cruise @@ -65,7 +66,8 @@ public: } MissionFlightStatus_t; Q_PROPERTY(QmlObjectListModel* visualItems READ visualItems NOTIFY visualItemsChanged) - Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) + Q_PROPERTY(QmlObjectListModel* waypointLines READ waypointLines NOTIFY waypointLinesChanged) ///< Used by Plan view only for interactive editing + Q_PROPERTY(QVariantList waypointPath READ waypointPath NOTIFY waypointPathChanged) ///< Used by Fly view only for static display Q_PROPERTY(QStringList complexMissionItemNames READ complexMissionItemNames NOTIFY complexMissionItemNamesChanged) Q_PROPERTY(QGeoCoordinate plannedHomePosition READ plannedHomePosition NOTIFY plannedHomePositionChanged) @@ -95,6 +97,11 @@ public: /// @return Sequence number for new item Q_INVOKABLE int insertSimpleMissionItem(QGeoCoordinate coordinate, int i); + /// Add a new ROI mission item to the list + /// @param i: index to insert at + /// @return Sequence number for new item + Q_INVOKABLE int insertROIMissionItem(QGeoCoordinate coordinate, int i); + /// Add a new complex mission item to the list /// @param itemName: Name of complex item to create (from complexMissionItemNames) /// @param mapCenterCoordinate: coordinate for current center of map @@ -118,7 +125,7 @@ public: bool loadTextFile(QFile& file, QString& errorString); // Overrides from PlanElementController - bool supported (void) const final { return true; }; + bool supported (void) const final { return true; } void start (bool editMode) final; void save (QJsonObject& json) final; bool load (const QJsonObject& json, QString& errorString) final; @@ -140,6 +147,7 @@ public: QmlObjectListModel* visualItems (void) { return _visualItems; } QmlObjectListModel* waypointLines (void) { return &_waypointLines; } + QVariantList waypointPath (void) { return _waypointPath; } QStringList complexMissionItemNames (void) const; QGeoCoordinate plannedHomePosition (void) const; VisualMissionItem* currentPlanViewItem (void) const; @@ -161,27 +169,28 @@ public: int batteriesRequired (void) const { return _missionFlightStatus.batteriesRequired; } ///< -1 for not supported signals: - void visualItemsChanged(void); - void waypointLinesChanged(void); - void newItemsFromVehicle(void); - void missionDistanceChanged(double missionDistance); - void missionTimeChanged(void); - void missionHoverDistanceChanged(double missionHoverDistance); - void missionHoverTimeChanged(void); - void missionCruiseDistanceChanged(double missionCruiseDistance); - void missionCruiseTimeChanged(void); - void missionMaxTelemetryChanged(double missionMaxTelemetry); - void complexMissionItemNamesChanged(void); - void resumeMissionIndexChanged(void); - void resumeMissionReady(void); - void resumeMissionUploadFail(void); - void batteryChangePointChanged(int batteryChangePoint); - void batteriesRequiredChanged(int batteriesRequired); - void plannedHomePositionChanged(QGeoCoordinate plannedHomePosition); - void progressPctChanged(double progressPct); - void currentMissionIndexChanged(int currentMissionIndex); - void currentPlanViewIndexChanged(); - void currentPlanViewItemChanged(); + void visualItemsChanged (void); + void waypointLinesChanged (void); + void waypointPathChanged (void); + void newItemsFromVehicle (void); + void missionDistanceChanged (double missionDistance); + void missionTimeChanged (void); + void missionHoverDistanceChanged (double missionHoverDistance); + void missionHoverTimeChanged (void); + void missionCruiseDistanceChanged (double missionCruiseDistance); + void missionCruiseTimeChanged (void); + void missionMaxTelemetryChanged (double missionMaxTelemetry); + void complexMissionItemNamesChanged (void); + void resumeMissionIndexChanged (void); + void resumeMissionReady (void); + void resumeMissionUploadFail (void); + void batteryChangePointChanged (int batteryChangePoint); + void batteriesRequiredChanged (int batteriesRequired); + void plannedHomePositionChanged (QGeoCoordinate plannedHomePosition); + void progressPctChanged (double progressPct); + void currentMissionIndexChanged (int currentMissionIndex); + void currentPlanViewIndexChanged (void); + void currentPlanViewItemChanged (void); private slots: void _newMissionItemsAvailableFromVehicle(bool removeAllRequested); @@ -218,7 +227,7 @@ private: bool _loadJsonMissionFileV2(const QJsonObject& json, QmlObjectListModel* visualItems, QString& errorString); bool _loadTextMissionFile(QTextStream& stream, QmlObjectListModel* visualItems, QString& errorString); int _nextSequenceNumber(void); - static void _scanForAdditionalSettings(QmlObjectListModel* visualItems, Vehicle* vehicle); + void _scanForAdditionalSettings(QmlObjectListModel* visualItems, Vehicle* vehicle); static bool _convertToMissionItems(QmlObjectListModel* visualMissionItems, QList& rgMissionItems, QObject* missionItemParent); void _setPlannedHomePositionFromFirstCoordinate(void); void _resetMissionFlightStatus(void); @@ -236,6 +245,7 @@ private: QmlObjectListModel* _visualItems; MissionSettingsItem* _settingsItem; QmlObjectListModel _waypointLines; + QVariantList _waypointPath; CoordVectHashTable _linesTable; bool _firstItemsFromVehicle; bool _itemsRequested; @@ -243,6 +253,7 @@ private: QString _surveyMissionItemName; QString _fwLandingMissionItemName; QString _structureScanMissionItemName; + QString _corridorScanMissionItemName; AppSettings* _appSettings; double _progressPct; int _currentPlanViewIndex; diff --git a/src/MissionManager/MissionItem.cc b/src/MissionManager/MissionItem.cc index 7696e0439d22ba9f2fa5b2c652ae5af516cb4a06..9eeacb1078bc17109dfe462c5d8a87a821bd3780 100644 --- a/src/MissionManager/MissionItem.cc +++ b/src/MissionManager/MissionItem.cc @@ -52,6 +52,7 @@ MissionItem::MissionItem(QObject* parent) setAutoContinue(true); + connect(&_param1Fact, &Fact::rawValueChanged, this, &MissionItem::_param1Changed); connect(&_param2Fact, &Fact::rawValueChanged, this, &MissionItem::_param2Changed); connect(&_param3Fact, &Fact::rawValueChanged, this, &MissionItem::_param3Changed); } @@ -170,10 +171,10 @@ bool MissionItem::load(QTextStream &loadStream) { const QStringList &wpParams = loadStream.readLine().split("\t"); if (wpParams.size() == 12) { + setCommand((MAV_CMD)wpParams[3].toInt()); // Has to be first since it triggers defaults to be set, which are then override by below set calls setSequenceNumber(wpParams[0].toInt()); setIsCurrentItem(wpParams[1].toInt() == 1 ? true : false); setFrame((MAV_FRAME)wpParams[2].toInt()); - setCommand((MAV_CMD)wpParams[3].toInt()); setParam1(wpParams[4].toDouble()); setParam2(wpParams[5].toDouble()); setParam3(wpParams[6].toDouble()); @@ -299,8 +300,8 @@ bool MissionItem::load(const QJsonObject& json, int sequenceNumber, QString& err } // Make sure to set these first since they can signal other changes - setFrame((MAV_FRAME)convertedJson[_jsonFrameKey].toInt()); setCommand((MAV_CMD)convertedJson[_jsonCommandKey].toInt()); + setFrame((MAV_FRAME)convertedJson[_jsonFrameKey].toInt()); _doJumpId = -1; if (convertedJson.contains(_jsonDoJumpIdKey)) { @@ -442,6 +443,27 @@ double MissionItem::specifiedGimbalYaw(void) const return gimbalYaw; } +double MissionItem::specifiedGimbalPitch(void) const +{ + double gimbalPitch = std::numeric_limits::quiet_NaN(); + + if (_commandFact.rawValue().toInt() == MAV_CMD_DO_MOUNT_CONTROL && _param7Fact.rawValue().toInt() == MAV_MOUNT_MODE_MAVLINK_TARGETING) { + gimbalPitch = _param1Fact.rawValue().toDouble(); + } + + return gimbalPitch; +} + +void MissionItem::_param1Changed(QVariant value) +{ + Q_UNUSED(value); + + double gimbalPitch = specifiedGimbalPitch(); + if (!qIsNaN(gimbalPitch)) { + emit specifiedGimbalPitchChanged(gimbalPitch); + } +} + void MissionItem::_param2Changed(QVariant value) { Q_UNUSED(value); diff --git a/src/MissionManager/MissionItem.h b/src/MissionManager/MissionItem.h index 78e07593aac0604214d072b890350fb6a60a2f52..8202a8480535ffdd41aaef21af3edb43fadfbc16 100644 --- a/src/MissionManager/MissionItem.h +++ b/src/MissionManager/MissionItem.h @@ -82,6 +82,9 @@ public: /// @return Flight gimbal yaw change value if this item supports it. If not it returns NaN. double specifiedGimbalYaw(void) const; + /// @return Flight gimbal pitch change value if this item supports it. If not it returns NaN. + double specifiedGimbalPitch(void) const; + void setCommand (MAV_CMD command); void setSequenceNumber (int sequenceNumber); void setIsCurrentItem (bool isCurrentItem); @@ -107,10 +110,12 @@ signals: void sequenceNumberChanged (int sequenceNumber); void specifiedFlightSpeedChanged(double flightSpeed); void specifiedGimbalYawChanged (double gimbalYaw); + void specifiedGimbalPitchChanged(double gimbalPitch); private slots: - void _param2Changed (QVariant value); - void _param3Changed (QVariant value); + void _param1Changed(QVariant value); + void _param2Changed(QVariant value); + void _param3Changed(QVariant value); private: bool _convertJsonV1ToV2(const QJsonObject& json, QJsonObject& v2Json, QString& errorString); diff --git a/src/MissionManager/MissionManager.cc b/src/MissionManager/MissionManager.cc index 0bf4501872909906dc004fbf5b6d2cf39fdc347e..e5fc3bd1ff83cd2dd837afca48da9b4660cb5175 100644 --- a/src/MissionManager/MissionManager.cc +++ b/src/MissionManager/MissionManager.cc @@ -214,11 +214,6 @@ void MissionManager::generateResumeMission(int resumeIndex) } _resumeMission = true; _writeMissionItemsWorker(); - - // Clean up no longer needed resume items - for (int i=0; ideleteLater(); - } } /// Called when a new mavlink message for out vehicle is received @@ -247,7 +242,7 @@ void MissionManager::_handleMissionCurrent(const mavlink_message_t& message) emit currentIndexChanged(_currentMissionIndex); } - if (_currentMissionIndex != _lastCurrentIndex) { + if (_currentMissionIndex != _lastCurrentIndex && _cachedLastCurrentIndex != _currentMissionIndex) { // We have to be careful of an RTL sequence causing a change of index to the DO_LAND_START sequence. This also triggers // a flight mode change away from mission flight mode. So we only update _lastCurrentIndex when the flight mode is mission. // But we can run into problems where we may get the MISSION_CURRENT message for the RTL/DO_LAND_START sequenc change prior diff --git a/src/MissionManager/MissionSettingsItem.cc b/src/MissionManager/MissionSettingsItem.cc index 8fa2708044c3aa5d0cd22cb9a375821aa99b94d4..abe14a452a8f4e67363b8592349c89a54f3c2161 100644 --- a/src/MissionManager/MissionSettingsItem.cc +++ b/src/MissionManager/MissionSettingsItem.cc @@ -57,14 +57,14 @@ MissionSettingsItem::MissionSettingsItem(Vehicle* vehicle, QObject* parent) connect(this, &MissionSettingsItem::terrainAltitudeChanged, this, &MissionSettingsItem::_setHomeAltFromTerrain); - connect(&_plannedHomePositionAltitudeFact, &Fact::valueChanged, this, &MissionSettingsItem::_setDirty); connect(&_plannedHomePositionAltitudeFact, &Fact::valueChanged, this, &MissionSettingsItem::_updateAltitudeInCoordinate); connect(&_cameraSection, &CameraSection::dirtyChanged, this, &MissionSettingsItem::_sectionDirtyChanged); connect(&_speedSection, &SpeedSection::dirtyChanged, this, &MissionSettingsItem::_sectionDirtyChanged); - connect(&_cameraSection, &CameraSection::specifiedGimbalYawChanged, this, &MissionSettingsItem::specifiedGimbalYawChanged); - connect(&_speedSection, &SpeedSection::specifiedFlightSpeedChanged, this, &MissionSettingsItem::specifiedFlightSpeedChanged); + connect(&_cameraSection, &CameraSection::specifiedGimbalYawChanged, this, &MissionSettingsItem::specifiedGimbalYawChanged); + connect(&_cameraSection, &CameraSection::specifiedGimbalPitchChanged, this, &MissionSettingsItem::specifiedGimbalPitchChanged); + connect(&_speedSection, &SpeedSection::specifiedFlightSpeedChanged, this, &MissionSettingsItem::specifiedFlightSpeedChanged); } int MissionSettingsItem::lastSequenceNumber(void) const @@ -259,6 +259,11 @@ double MissionSettingsItem::specifiedGimbalYaw(void) return _cameraSection.specifyGimbal() ? _cameraSection.gimbalYaw()->rawValue().toDouble() : std::numeric_limits::quiet_NaN(); } +double MissionSettingsItem::specifiedGimbalPitch(void) +{ + return _cameraSection.specifyGimbal() ? _cameraSection.gimbalPitch()->rawValue().toDouble() : std::numeric_limits::quiet_NaN(); +} + void MissionSettingsItem::_updateAltitudeInCoordinate(QVariant value) { double newAltitude = value.toDouble(); diff --git a/src/MissionManager/MissionSettingsItem.h b/src/MissionManager/MissionSettingsItem.h index c61a6d7e7ad5b11e642adc8052c37fbc98a2789f..d20410917ddcd245265d2acd330b22d86a3ce5ce 100644 --- a/src/MissionManager/MissionSettingsItem.h +++ b/src/MissionManager/MissionSettingsItem.h @@ -73,6 +73,7 @@ public: QGeoCoordinate exitCoordinate (void) const final { return _plannedHomePositionCoordinate; } int sequenceNumber (void) const final { return _sequenceNumber; } double specifiedGimbalYaw (void) final; + double specifiedGimbalPitch (void) final; void appendMissionItems (QList& items, QObject* missionItemParent) final; void applyNewAltitude (double newAltitude) final { Q_UNUSED(newAltitude); /* no action */ } double specifiedFlightSpeed (void) final; diff --git a/src/MissionManager/PlanManager.cc b/src/MissionManager/PlanManager.cc index 6ad7fbfd0d8c1bcd91b482d6b000e75c8c5d0d45..b6103ea68f9669f5c3bcc9f0f3f89b717c526b72 100644 --- a/src/MissionManager/PlanManager.cc +++ b/src/MissionManager/PlanManager.cc @@ -30,13 +30,13 @@ PlanManager::PlanManager(Vehicle* vehicle, MAV_MISSION_TYPE planType) , _transactionInProgress(TransactionNone) , _resumeMission(false) , _lastMissionRequest(-1) + , _missionItemCountToRead(-1) , _currentMissionIndex(-1) , _lastCurrentIndex(-1) { _ackTimeoutTimer = new QTimer(this); _ackTimeoutTimer->setSingleShot(true); - _ackTimeoutTimer->setInterval(_ackTimeoutMilliseconds); - + connect(_ackTimeoutTimer, &QTimer::timeout, this, &PlanManager::_ackTimeout); } @@ -85,8 +85,8 @@ void PlanManager::writeMissionItems(const QList& missionItems) int firstIndex = skipFirstItem ? 1 : 0; for (int i=firstIndex; isetIsCurrentItem(i == firstIndex); @@ -244,6 +244,24 @@ void PlanManager::_ackTimeout(void) void PlanManager::_startAckTimeout(AckType_t ack) { + switch (ack) { + case AckMissionItem: + // We are actively trying to get the mission item, so we don't want to wait as long. + _ackTimeoutTimer->setInterval(_retryTimeoutMilliseconds); + break; + case AckNone: + // FALLTHROUGH + case AckMissionCount: + // FALLTHROUGH + case AckMissionRequest: + // FALLTHROUGH + case AckMissionClearAll: + // FALLTHROUGH + case AckGuidedItem: + _ackTimeoutTimer->setInterval(_ackTimeoutMilliseconds); + break; + } + _expectedAck = ack; _ackTimeoutTimer->start(); } @@ -263,7 +281,7 @@ bool PlanManager::_checkForExpectedAck(AckType_t receivedAck) } else { // We just warn in this case, this could be crap left over from a previous transaction or the vehicle going bonkers. // Whatever it is we let the ack timeout handle any error output to the user. - qCDebug(PlanManagerLog) << QString("Out of sequence ack expected:received %1:%2 %1").arg(_ackTypeToString(_expectedAck)).arg(_ackTypeToString(receivedAck)).arg(_planTypeString()); + qCDebug(PlanManagerLog) << QString("Out of sequence ack %1 expected:received %2:%3").arg(_planTypeString().arg(_ackTypeToString(_expectedAck)).arg(_ackTypeToString(receivedAck))); } return false; } @@ -317,6 +335,7 @@ void PlanManager::_handleMissionCount(const mavlink_message_t& message) for (int i=0; ideleteLater(); + // Using deleteLater here causes too much transient memory to stack up + delete _missionItems[i]; } _missionItems.clear(); } @@ -920,7 +940,8 @@ void PlanManager::_clearAndDeleteMissionItems(void) void PlanManager::_clearAndDeleteWriteMissionItems(void) { for (int i=0; i<_writeMissionItems.count(); i++) { - _writeMissionItems[i]->deleteLater(); + // Using deleteLater here causes too much transient memory to stack up + delete _writeMissionItems[i]; } _writeMissionItems.clear(); } diff --git a/src/MissionManager/PlanManager.h b/src/MissionManager/PlanManager.h index aeda03a50e735beb2a2f5b9f0ebeca06c513965b..b5c53b52aea76bf04e5a0c10c9d8e11c17419ff5 100644 --- a/src/MissionManager/PlanManager.h +++ b/src/MissionManager/PlanManager.h @@ -28,11 +28,11 @@ Q_DECLARE_LOGGING_CATEGORY(PlanManagerLog) class PlanManager : public QObject { Q_OBJECT - + public: PlanManager(Vehicle* vehicle, MAV_MISSION_TYPE planType); ~PlanManager(); - + bool inProgress(void) const; const QList& missionItems(void) { return _missionItems; } @@ -41,12 +41,13 @@ public: /// Last current mission item reported while in Mission flight mode int lastCurrentIndex(void) const { return _lastCurrentIndex; } - + /// Load the mission items from the vehicle /// Signals newMissionItemsAvailable when done void loadFromVehicle(void); - + /// Writes the specified set of mission items to the vehicle + /// IMPORTANT NOTE: PlanManager will take control of the MissionItem objects with the missionItems list. It will free them when done. /// @param missionItems Items to send to vehicle /// Signals sendComplete when done void writeMissionItems(const QList& missionItems); @@ -69,9 +70,12 @@ public: } ErrorCode_t; // These values are public so the unit test can set appropriate signal wait times + // When passively waiting for a mission process, use a longer timeout. static const int _ackTimeoutMilliseconds = 1000; + // When actively retrying to request mission items, use a shorter timeout instead. + static const int _retryTimeoutMilliseconds = 250; static const int _maxRetryCount = 5; - + signals: void newMissionItemsAvailable (bool removeAllRequested); void inProgressChanged (bool inProgress); @@ -87,7 +91,7 @@ signals: private slots: void _mavlinkMessageReceived(const mavlink_message_t& message); void _ackTimeout(void); - + protected: typedef enum { AckNone, ///< State machine is idle @@ -133,17 +137,18 @@ protected: Vehicle* _vehicle; MAV_MISSION_TYPE _planType; LinkInterface* _dedicatedLink; - + QTimer* _ackTimeoutTimer; AckType_t _expectedAck; int _retryCount; - + TransactionType_t _transactionInProgress; bool _resumeMission; QList _itemIndicesToWrite; ///< List of mission items which still need to be written to vehicle QList _itemIndicesToRead; ///< List of mission items which still need to be requested from vehicle int _lastMissionRequest; ///< Index of item last requested by MISSION_REQUEST - + int _missionItemCountToRead;///< Count of all mission items to read + QList _missionItems; ///< Set of mission items on vehicle QList _writeMissionItems; ///< Set of mission items currently being written to vehicle int _currentMissionIndex; diff --git a/src/MissionManager/PlanMasterController.cc b/src/MissionManager/PlanMasterController.cc index ca5bfe040d9a68579edc295b83d0a48a527978b4..5f788998af9432b67c6aa795faac1d1167dbdae3 100644 --- a/src/MissionManager/PlanMasterController.cc +++ b/src/MissionManager/PlanMasterController.cc @@ -157,6 +157,11 @@ void PlanMasterController::_activeVehicleChanged(Vehicle* activeVehicle) void PlanMasterController::loadFromVehicle(void) { + if (_managerVehicle->highLatencyLink()) { + qgcApp()->showMessage(tr("Download not supported on high latency links.")); + return; + } + if (offline()) { qCWarning(PlanMasterControllerLog) << "PlanMasterController::loadFromVehicle called while offline"; } else if (!_editMode) { @@ -256,6 +261,11 @@ void PlanMasterController::_sendRallyPointsComplete(void) void PlanMasterController::sendToVehicle(void) { + if (_managerVehicle->highLatencyLink()) { + qgcApp()->showMessage(tr("Upload not supported on high latency links.")); + return; + } + if (offline()) { qCWarning(PlanMasterControllerLog) << "PlanMasterController::sendToVehicle called while offline"; } else if (syncInProgress()) { @@ -273,7 +283,7 @@ void PlanMasterController::sendToVehicle(void) void PlanMasterController::loadFromFile(const QString& filename) { QString errorString; - QString errorMessage = tr("Error reading Plan file (%1). %2").arg(filename).arg("%1"); + QString errorMessage = tr("Error loading Plan file (%1). %2").arg(filename).arg("%1"); if (filename.isEmpty()) { return; @@ -335,6 +345,22 @@ void PlanMasterController::loadFromFile(const QString& filename) } } +QJsonDocument PlanMasterController::saveToJson() +{ + QJsonObject planJson; + QJsonObject missionJson; + QJsonObject fenceJson; + QJsonObject rallyJson; + JsonHelper::saveQGCJsonFileHeader(planJson, _planFileType, _planFileVersion); + _missionController.save(missionJson); + _geoFenceController.save(fenceJson); + _rallyPointController.save(rallyJson); + planJson[_jsonMissionObjectKey] = missionJson; + planJson[_jsonGeoFenceObjectKey] = fenceJson; + planJson[_jsonRallyPointsObjectKey] = rallyJson; + return QJsonDocument(planJson); +} + void PlanMasterController::saveToFile(const QString& filename) { if (filename.isEmpty()) { @@ -351,20 +377,7 @@ void PlanMasterController::saveToFile(const QString& filename) if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { qgcApp()->showMessage(tr("Plan save error %1 : %2").arg(filename).arg(file.errorString())); } else { - QJsonObject planJson; - QJsonObject missionJson; - QJsonObject fenceJson; - QJsonObject rallyJson; - - JsonHelper::saveQGCJsonFileHeader(planJson, _planFileType, _planFileVersion); - _missionController.save(missionJson); - _geoFenceController.save(fenceJson); - _rallyPointController.save(rallyJson); - planJson[_jsonMissionObjectKey] = missionJson; - planJson[_jsonGeoFenceObjectKey] = fenceJson; - planJson[_jsonRallyPointsObjectKey] = rallyJson; - - QJsonDocument saveDoc(planJson); + QJsonDocument saveDoc = saveToJson(); file.write(saveDoc.toJson()); } diff --git a/src/MissionManager/PlanMasterController.h b/src/MissionManager/PlanMasterController.h index 03ba7b6e3f3c16ab8612dfb965abe63619e5bcc6..30dd38144326ac1cf87596bba410a92bd5f3858e 100644 --- a/src/MissionManager/PlanMasterController.h +++ b/src/MissionManager/PlanMasterController.h @@ -80,6 +80,8 @@ public: QStringList saveNameFilters (void) const; QStringList saveKmlFilters (void) const; + QJsonDocument saveToJson (); + Vehicle* controllerVehicle(void) { return _controllerVehicle; } Vehicle* managerVehicle(void) { return _managerVehicle; } diff --git a/src/MissionManager/QGCMapPolygon.cc b/src/MissionManager/QGCMapPolygon.cc index aa1e0b3898a2f0afd2dc9d81ea6bb26c008fd94a..3803e203ed08495ae8d2d3cb62dd9728999a143b 100644 --- a/src/MissionManager/QGCMapPolygon.cc +++ b/src/MissionManager/QGCMapPolygon.cc @@ -302,9 +302,10 @@ void QGCMapPolygon::_updateCenter(void) } center = _coordFromPointF(QPointF(centroid.x() / polygonF.count(), centroid.y() / polygonF.count())); } - - _center = center; - emit centerChanged(center); + if (_center != center) { + _center = center; + emit centerChanged(center); + } } } @@ -361,7 +362,7 @@ QGeoCoordinate QGCMapPolygon::vertexCoordinate(int vertex) const } } -QList QGCMapPolygon::nedPolygon(void) +QList QGCMapPolygon::nedPolygon(void) const { QList nedPolygon; @@ -514,3 +515,19 @@ bool QGCMapPolygon::loadKMLFile(const QString& kmlFile) return true; } + +double QGCMapPolygon::area(void) const +{ + // https://www.mathopenref.com/coordpolygonarea2.html + + double coveredArea = 0.0; + QList nedVertices = nedPolygon(); + for (int i=0; i nedPolygon(void); + QList nedPolygon(void) const; + + /// Returns the area of the polygon in meters squared + double area(void) const; // Property methods diff --git a/src/MissionManager/QGCMapPolygonTest.cc b/src/MissionManager/QGCMapPolygonTest.cc index d9160c9d5e24d6c27b7a077d283778db6814941c..4de4c5ee8eabad481c549b18f08fe09b27a18824 100644 --- a/src/MissionManager/QGCMapPolygonTest.cc +++ b/src/MissionManager/QGCMapPolygonTest.cc @@ -122,7 +122,12 @@ void QGCMapPolygonTest::_testVertexManipulation(void) QCOMPARE(_mapPolygon->count(), i); _mapPolygon->appendVertex(_polyPoints[i]); - QVERIFY(_multiSpyPolygon->checkOnlySignalByMask(pathChangedMask | polygonDirtyChangedMask | polygonCountChangedMask | centerChangedMask)); + if (i >= 2) { + // Center is no recalculated until there are 3 points or more + QVERIFY(_multiSpyPolygon->checkOnlySignalByMask(pathChangedMask | polygonDirtyChangedMask | polygonCountChangedMask | centerChangedMask)); + } else { + QVERIFY(_multiSpyPolygon->checkOnlySignalByMask(pathChangedMask | polygonDirtyChangedMask | polygonCountChangedMask)); + } QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask | modelCountChangedMask)); QCOMPARE(_multiSpyPolygon->pullIntFromSignalIndex(polygonCountChangedIndex), i+1); QCOMPARE(_multiSpyModel->pullIntFromSignalIndex(modelCountChangedIndex), i+1); @@ -200,17 +205,17 @@ void QGCMapPolygonTest::_testVertexManipulation(void) void QGCMapPolygonTest::_testKMLLoad(void) { - QVERIFY(_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/GoodPolygon.kml"))); + QVERIFY(_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/PolygonGood.kml"))); setExpectedMessageBox(QMessageBox::Ok); - QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/BadXml.kml"))); + QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/PolygonBadXml.kml"))); checkExpectedMessageBox(); setExpectedMessageBox(QMessageBox::Ok); - QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/MissingPolygonNode.kml"))); + QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/PolygonMissingNode.kml"))); checkExpectedMessageBox(); setExpectedMessageBox(QMessageBox::Ok); - QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/BadCoordinatesNode.kml"))); + QVERIFY(!_mapPolygon->loadKMLFile(QStringLiteral(":/unittest/PolygonBadCoordinatesNode.kml"))); checkExpectedMessageBox(); } diff --git a/src/MissionManager/QGCMapPolygonVisuals.qml b/src/MissionManager/QGCMapPolygonVisuals.qml index 0c5300f523d4371ecfce839e154e1e6e2975724a..c0bd2ee7ba343c155f1b878d930e00f14349ad26 100644 --- a/src/MissionManager/QGCMapPolygonVisuals.qml +++ b/src/MissionManager/QGCMapPolygonVisuals.qml @@ -11,6 +11,7 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtLocation 5.3 import QtPositioning 5.3 +import QtQuick.Dialogs 1.2 import QGroundControl 1.0 import QGroundControl.ScreenTools 1.0 @@ -147,10 +148,6 @@ Item { setCircleRadius(center, radius) } - function loadKMLFile() { - mapPolygon.loadKMLFile("/Users/Don/Downloads/polygon.kml") - } - onInteractiveChanged: { if (interactive) { addHandles() @@ -180,8 +177,7 @@ Item { title: qsTr("Select KML File") selectExisting: true nameFilters: [ qsTr("KML files (*.kml)") ] - - + fileExtension: "kml" onAcceptedForLoad: { mapPolygon.loadKMLFile(file) close() @@ -378,6 +374,15 @@ Item { } } + Component { + id: editPositionDialog + + EditPositionDialog { + coordinate: mapPolygon.center + onCoordinateChanged: mapPolygon.center = coordinate + } + } + Component { id: centerDragAreaComponent @@ -408,10 +413,16 @@ Item { MenuItem { text: qsTr("Set radius..." ) - enabled: _circle + visible: _circle onTriggered: radiusDialog.visible = true } + MenuItem { + text: qsTr("Edit position..." ) + enabled: _circle + onTriggered: qgcView.showDialog(editPositionDialog, qsTr("Edit Position"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) + } + MenuItem { text: qsTr("Load KML...") onTriggered: kmlLoadDialog.openForLoad() @@ -441,6 +452,7 @@ Item { id: radiusField text: _circleRadius.toFixed(2) onEditingFinished: setRadiusFromDialog() + inputMethodHints: Qt.ImhFormattedNumbersOnly } } diff --git a/src/MissionManager/QGCMapPolyline.cc b/src/MissionManager/QGCMapPolyline.cc new file mode 100644 index 0000000000000000000000000000000000000000..cb3a39ef8107a35e42d373b7a4d38d6a308ccf29 --- /dev/null +++ b/src/MissionManager/QGCMapPolyline.cc @@ -0,0 +1,440 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "QGCMapPolyline.h" +#include "QGCGeo.h" +#include "JsonHelper.h" +#include "QGCQGeoCoordinate.h" +#include "QGCApplication.h" + +#include +#include +#include +#include +#include +#include + +const char* QGCMapPolyline::jsonPolylineKey = "polyline"; + +QGCMapPolyline::QGCMapPolyline(QObject* parent) + : QObject (parent) + , _dirty (false) + , _interactive (false) +{ + _init(); +} + +QGCMapPolyline::QGCMapPolyline(const QGCMapPolyline& other, QObject* parent) + : QObject (parent) + , _dirty (false) + , _interactive (false) +{ + *this = other; + + _init(); +} + +const QGCMapPolyline& QGCMapPolyline::operator=(const QGCMapPolyline& other) +{ + clear(); + + QVariantList vertices = other.path(); + for (int i=0; i()); + } + + setDirty(true); + + return *this; +} + +void QGCMapPolyline::_init(void) +{ + connect(&_polylineModel, &QmlObjectListModel::dirtyChanged, this, &QGCMapPolyline::_polylineModelDirtyChanged); + connect(&_polylineModel, &QmlObjectListModel::countChanged, this, &QGCMapPolyline::_polylineModelCountChanged); +} + +void QGCMapPolyline::clear(void) +{ + _polylinePath.clear(); + emit pathChanged(); + + _polylineModel.clearAndDeleteContents(); + + emit cleared(); + + setDirty(true); +} + +void QGCMapPolyline::adjustVertex(int vertexIndex, const QGeoCoordinate coordinate) +{ + _polylinePath[vertexIndex] = QVariant::fromValue(coordinate); + emit pathChanged(); + _polylineModel.value(vertexIndex)->setCoordinate(coordinate); + setDirty(true); +} + +void QGCMapPolyline::setDirty(bool dirty) +{ + if (_dirty != dirty) { + _dirty = dirty; + if (!dirty) { + _polylineModel.setDirty(false); + } + emit dirtyChanged(dirty); + } +} +QGeoCoordinate QGCMapPolyline::_coordFromPointF(const QPointF& point) const +{ + QGeoCoordinate coord; + + if (_polylinePath.count() > 0) { + QGeoCoordinate tangentOrigin = _polylinePath[0].value(); + convertNedToGeo(-point.y(), point.x(), 0, tangentOrigin, &coord); + } + + return coord; +} + +QPointF QGCMapPolyline::_pointFFromCoord(const QGeoCoordinate& coordinate) const +{ + if (_polylinePath.count() > 0) { + double y, x, down; + QGeoCoordinate tangentOrigin = _polylinePath[0].value(); + + convertGeoToNed(coordinate, tangentOrigin, &y, &x, &down); + return QPointF(x, -y); + } + + return QPointF(); +} + +void QGCMapPolyline::setPath(const QList& path) +{ + _polylinePath.clear(); + _polylineModel.clearAndDeleteContents(); + foreach (const QGeoCoordinate& coord, path) { + _polylinePath.append(QVariant::fromValue(coord)); + _polylineModel.append(new QGCQGeoCoordinate(coord, this)); + } + + setDirty(true); + emit pathChanged(); +} + +void QGCMapPolyline::setPath(const QVariantList& path) +{ + _polylinePath = path; + + _polylineModel.clearAndDeleteContents(); + for (int i=0; i<_polylinePath.count(); i++) { + _polylineModel.append(new QGCQGeoCoordinate(_polylinePath[i].value(), this)); + } + + setDirty(true); + emit pathChanged(); +} + + +void QGCMapPolyline::saveToJson(QJsonObject& json) +{ + QJsonValue jsonValue; + + JsonHelper::saveGeoCoordinateArray(_polylinePath, false /* writeAltitude*/, jsonValue); + json.insert(jsonPolylineKey, jsonValue); + setDirty(false); +} + +bool QGCMapPolyline::loadFromJson(const QJsonObject& json, bool required, QString& errorString) +{ + errorString.clear(); + clear(); + + if (required) { + if (!JsonHelper::validateRequiredKeys(json, QStringList(jsonPolylineKey), errorString)) { + return false; + } + } else if (!json.contains(jsonPolylineKey)) { + return true; + } + + if (!JsonHelper::loadGeoCoordinateArray(json[jsonPolylineKey], false /* altitudeRequired */, _polylinePath, errorString)) { + return false; + } + + for (int i=0; i<_polylinePath.count(); i++) { + _polylineModel.append(new QGCQGeoCoordinate(_polylinePath[i].value(), this)); + } + + setDirty(false); + emit pathChanged(); + + return true; +} + +QList QGCMapPolyline::coordinateList(void) const +{ + QList coords; + + for (int i=0; i<_polylinePath.count(); i++) { + coords.append(_polylinePath[i].value()); + } + + return coords; +} + +void QGCMapPolyline::splitSegment(int vertexIndex) +{ + int nextIndex = vertexIndex + 1; + if (nextIndex > _polylinePath.length() - 1) { + return; + } + + QGeoCoordinate firstVertex = _polylinePath[vertexIndex].value(); + QGeoCoordinate nextVertex = _polylinePath[nextIndex].value(); + + double distance = firstVertex.distanceTo(nextVertex); + double azimuth = firstVertex.azimuthTo(nextVertex); + QGeoCoordinate newVertex = firstVertex.atDistanceAndAzimuth(distance / 2, azimuth); + + if (nextIndex == 0) { + appendVertex(newVertex); + } else { + _polylineModel.insert(nextIndex, new QGCQGeoCoordinate(newVertex, this)); + _polylinePath.insert(nextIndex, QVariant::fromValue(newVertex)); + emit pathChanged(); + } +} + +void QGCMapPolyline::appendVertex(const QGeoCoordinate& coordinate) +{ + _polylinePath.append(QVariant::fromValue(coordinate)); + _polylineModel.append(new QGCQGeoCoordinate(coordinate, this)); + emit pathChanged(); +} + +void QGCMapPolyline::removeVertex(int vertexIndex) +{ + if (vertexIndex < 0 || vertexIndex > _polylinePath.length() - 1) { + qWarning() << "Call to removeVertex with bad vertexIndex:count" << vertexIndex << _polylinePath.length(); + return; + } + + if (_polylinePath.length() <= 2) { + // Don't allow the user to trash the polyline + return; + } + + QObject* coordObj = _polylineModel.removeAt(vertexIndex); + coordObj->deleteLater(); + + _polylinePath.removeAt(vertexIndex); + emit pathChanged(); +} + +void QGCMapPolyline::setInteractive(bool interactive) +{ + if (_interactive != interactive) { + _interactive = interactive; + emit interactiveChanged(interactive); + } +} + +QGeoCoordinate QGCMapPolyline::vertexCoordinate(int vertex) const +{ + if (vertex >= 0 && vertex < _polylinePath.count()) { + return _polylinePath[vertex].value(); + } else { + qWarning() << "QGCMapPolyline::vertexCoordinate bad vertex requested"; + return QGeoCoordinate(); + } +} + +QList QGCMapPolyline::nedPolyline(void) +{ + QList nedPolyline; + + if (count() > 0) { + QGeoCoordinate tangentOrigin = vertexCoordinate(0); + + for (int i=0; i<_polylinePath.count(); i++) { + double y, x, down; + QGeoCoordinate vertex = vertexCoordinate(i); + if (i == 0) { + // This avoids a nan calculation that comes out of convertGeoToNed + x = y = 0; + } else { + convertGeoToNed(vertex, tangentOrigin, &y, &x, &down); + } + nedPolyline += QPointF(x, y); + } + } + + return nedPolyline; +} + + +QList QGCMapPolyline::offsetPolyline(double distance) +{ + QList rgNewPolyline; + + // I'm sure there is some beautiful famous algorithm to do this, but here is a brute force method + + if (count() > 1) { + // Convert the polygon to NED + QList rgNedVertices = nedPolyline(); + + // Walk the edges, offsetting by the specified distance + QList rgOffsetEdges; + for (int i=0; ishowMessage(tr("File not found: %1").arg(kmlFile)); + return false; + } + + if (!file.open(QIODevice::ReadOnly)) { + qgcApp()->showMessage(tr("Unable to open file: %1 error: $%2").arg(kmlFile).arg(file.errorString())); + return false; + } + + QDomDocument doc; + QString errorMessage; + int errorLine; + if (!doc.setContent(&file, &errorMessage, &errorLine)) { + qgcApp()->showMessage(tr("Unable to parse KML file: %1 error: %2 line: %3").arg(kmlFile).arg(errorMessage).arg(errorLine)); + return false; + } + + QDomNodeList rgNodes = doc.elementsByTagName("Polygon"); + if (rgNodes.count() == 0) { + qgcApp()->showMessage(tr("Unable to find Polygon node in KML")); + return false; + } + + QDomNode coordinatesNode = rgNodes.item(0).namedItem("outerBoundaryIs").namedItem("LinearRing").namedItem("coordinates"); + if (coordinatesNode.isNull()) { + qgcApp()->showMessage(tr("Internal error: Unable to find coordinates node in KML")); + return false; + } + + QString coordinatesString = coordinatesNode.toElement().text().simplified(); + QStringList rgCoordinateStrings = coordinatesString.split(" "); + + QList rgCoords; + for (int i=0; i rgReversed; + + for (int i=0; i(); + QGeoCoordinate to = _polylinePath[i+1].value(); + length += from.distanceTo(to); + } + + return length; +} diff --git a/src/MissionManager/QGCMapPolyline.h b/src/MissionManager/QGCMapPolyline.h new file mode 100644 index 0000000000000000000000000000000000000000..f4ec936b2e2bebaab59f84c0d08d4f7b970d4ad3 --- /dev/null +++ b/src/MissionManager/QGCMapPolyline.h @@ -0,0 +1,114 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "QmlObjectListModel.h" + +class QGCMapPolyline : public QObject +{ + Q_OBJECT + +public: + QGCMapPolyline(QObject* parent = NULL); + QGCMapPolyline(const QGCMapPolyline& other, QObject* parent = NULL); + + const QGCMapPolyline& operator=(const QGCMapPolyline& other); + + Q_PROPERTY(int count READ count NOTIFY countChanged) + Q_PROPERTY(QVariantList path READ path NOTIFY pathChanged) + Q_PROPERTY(QmlObjectListModel* pathModel READ qmlPathModel CONSTANT) + Q_PROPERTY(bool dirty READ dirty WRITE setDirty NOTIFY dirtyChanged) + Q_PROPERTY(bool interactive READ interactive WRITE setInteractive NOTIFY interactiveChanged) + + Q_INVOKABLE void clear(void); + Q_INVOKABLE void appendVertex(const QGeoCoordinate& coordinate); + Q_INVOKABLE void removeVertex(int vertexIndex); + + /// Adjust the value for the specified coordinate + /// @param vertexIndex Polygon point index to modify (0-based) + /// @param coordinate New coordinate for point + Q_INVOKABLE void adjustVertex(int vertexIndex, const QGeoCoordinate coordinate); + + /// Splits the line segment comprised of vertexIndex -> vertexIndex + 1 + Q_INVOKABLE void splitSegment(int vertexIndex); + + /// Offsets the current polyline edges by the specified distance in meters + /// @return Offset set of vertices + QList offsetPolyline(double distance); + + /// Loads a polyline from a KML file + /// @return true: success + Q_INVOKABLE bool loadKMLFile(const QString& kmlFile); + + /// Returns the path in a list of QGeoCoordinate's format + QList coordinateList(void) const; + + /// Returns the QGeoCoordinate for the vertex specified + QGeoCoordinate vertexCoordinate(int vertex) const; + + /// Saves the polyline to the json object. + /// @param json Json object to save to + void saveToJson(QJsonObject& json); + + /// Load a polyline from json + /// @param json Json object to load from + /// @param required true: no polygon in object will generate error + /// @param errorString Error string if return is false + /// @return true: success, false: failure (errorString set) + bool loadFromJson(const QJsonObject& json, bool required, QString& errorString); + + /// Convert polyline to NED and return (D is ignored) + QList nedPolyline(void); + + /// Returns the length of the polyline in meters + double length(void) const; + + // Property methods + + int count (void) const { return _polylinePath.count(); } + bool dirty (void) const { return _dirty; } + void setDirty (bool dirty); + bool interactive (void) const { return _interactive; } + QVariantList path (void) const { return _polylinePath; } + + QmlObjectListModel* qmlPathModel(void) { return &_polylineModel; } + QmlObjectListModel& pathModel (void) { return _polylineModel; } + + void setPath (const QList& path); + void setPath (const QVariantList& path); + void setInteractive (bool interactive); + + static const char* jsonPolylineKey; + +signals: + void countChanged (int count); + void pathChanged (void); + void dirtyChanged (bool dirty); + void cleared (void); + void interactiveChanged (bool interactive); + +private slots: + void _polylineModelCountChanged(int count); + void _polylineModelDirtyChanged(bool dirty); + +private: + void _init(void); + QGeoCoordinate _coordFromPointF(const QPointF& point) const; + QPointF _pointFFromCoord(const QGeoCoordinate& coordinate) const; + + QVariantList _polylinePath; + QmlObjectListModel _polylineModel; + bool _dirty; + bool _interactive; +}; diff --git a/src/MissionManager/QGCMapPolylineTest.cc b/src/MissionManager/QGCMapPolylineTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..e53ee73ead9bffe3be49de09d208452b89a88d4b --- /dev/null +++ b/src/MissionManager/QGCMapPolylineTest.cc @@ -0,0 +1,202 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "QGCMapPolylineTest.h" +#include "QGCApplication.h" +#include "QGCQGeoCoordinate.h" + +QGCMapPolylineTest::QGCMapPolylineTest(void) +{ + _linePoints << QGeoCoordinate(47.635638361473475, -122.09269407980834 ) << + QGeoCoordinate(47.635638361473475, -122.08545246602667) << + QGeoCoordinate(47.63057923872075, -122.08545246602667) << + QGeoCoordinate(47.63057923872075, -122.09269407980834); +} + +void QGCMapPolylineTest::init(void) +{ + UnitTest::init(); + + _rgSignals[countChangedIndex] = SIGNAL(countChanged(int)); + _rgSignals[pathChangedIndex] = SIGNAL(pathChanged()); + _rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); + _rgSignals[clearedIndex] = SIGNAL(cleared()); + + _rgModelSignals[modelCountChangedIndex] = SIGNAL(countChanged(int)); + _rgModelSignals[modelDirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); + + _mapPolyline = new QGCMapPolyline(this); + _pathModel = _mapPolyline->qmlPathModel(); + QVERIFY(_pathModel); + + _multiSpyPolyline = new MultiSignalSpy(); + QCOMPARE(_multiSpyPolyline->init(_mapPolyline, _rgSignals, _cSignals), true); + + _multiSpyModel = new MultiSignalSpy(); + QCOMPARE(_multiSpyModel->init(_pathModel, _rgModelSignals, _cModelSignals), true); +} + +void QGCMapPolylineTest::cleanup(void) +{ + delete _mapPolyline; + delete _multiSpyPolyline; + delete _multiSpyModel; +} + +void QGCMapPolylineTest::_testDirty(void) +{ + // Check basic dirty bit set/get + + QVERIFY(!_mapPolyline->dirty()); + QVERIFY(!_pathModel->dirty()); + + _mapPolyline->setDirty(false); + QVERIFY(!_mapPolyline->dirty()); + QVERIFY(!_pathModel->dirty()); + QVERIFY(_multiSpyPolyline->checkNoSignals()); + QVERIFY(_multiSpyModel->checkNoSignals()); + + _mapPolyline->setDirty(true); + QVERIFY(_mapPolyline->dirty()); + QVERIFY(!_pathModel->dirty()); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpyPolyline->pullBoolFromSignalIndex(dirtyChangedIndex)); + QVERIFY(_multiSpyModel->checkNoSignals()); + _multiSpyPolyline->clearAllSignals(); + + _mapPolyline->setDirty(false); + QVERIFY(!_mapPolyline->dirty()); + QVERIFY(!_pathModel->dirty()); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(!_multiSpyPolyline->pullBoolFromSignalIndex(dirtyChangedIndex)); + QVERIFY(_multiSpyModel->checkNoSignals()); + _multiSpyPolyline->clearAllSignals(); + + _pathModel->setDirty(true); + QVERIFY(_pathModel->dirty()); + QVERIFY(_mapPolyline->dirty()); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpyPolyline->pullBoolFromSignalIndex(dirtyChangedIndex)); + QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask)); + QVERIFY(_multiSpyModel->pullBoolFromSignalIndex(modelDirtyChangedIndex)); + _multiSpyPolyline->clearAllSignals(); + _multiSpyModel->clearAllSignals(); + + _mapPolyline->setDirty(false); + QVERIFY(!_mapPolyline->dirty()); + QVERIFY(!_pathModel->dirty()); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(!_multiSpyPolyline->pullBoolFromSignalIndex(dirtyChangedIndex)); + QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask)); + QVERIFY(!_multiSpyModel->pullBoolFromSignalIndex(modelDirtyChangedIndex)); + _multiSpyPolyline->clearAllSignals(); + _multiSpyModel->clearAllSignals(); +} + +void QGCMapPolylineTest::_testVertexManipulation(void) +{ + // Vertex addition testing + + for (int i=0; i<_linePoints.count(); i++) { + QCOMPARE(_mapPolyline->count(), i); + + _mapPolyline->appendVertex(_linePoints[i]); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(pathChangedMask | dirtyChangedMask | countChangedMask)); + QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask | modelCountChangedMask)); + QCOMPARE(_multiSpyPolyline->pullIntFromSignalIndex(countChangedIndex), i+1); + QCOMPARE(_multiSpyModel->pullIntFromSignalIndex(modelCountChangedIndex), i+1); + + QVERIFY(_mapPolyline->dirty()); + QVERIFY(_pathModel->dirty()); + + QCOMPARE(_mapPolyline->count(), i+1); + + QVariantList vertexList = _mapPolyline->path(); + QCOMPARE(vertexList.count(), i+1); + QCOMPARE(vertexList[i].value(), _linePoints[i]); + + QCOMPARE(_pathModel->count(), i+1); + QCOMPARE(_pathModel->value(i)->coordinate(), _linePoints[i]); + + _mapPolyline->setDirty(false); + _multiSpyPolyline->clearAllSignals(); + _multiSpyModel->clearAllSignals(); + } + + // Vertex adjustment testing + + QGCQGeoCoordinate* geoCoord = _pathModel->value(1); + QSignalSpy coordSpy(geoCoord, SIGNAL(coordinateChanged(QGeoCoordinate))); + QSignalSpy coordDirtySpy(geoCoord, SIGNAL(dirtyChanged(bool))); + QGeoCoordinate adjustCoord(_linePoints[1].latitude() + 1, _linePoints[1].longitude() + 1); + _mapPolyline->adjustVertex(1, adjustCoord); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(pathChangedMask | dirtyChangedMask)); + QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask)); + QCOMPARE(coordSpy.count(), 1); + QCOMPARE(coordDirtySpy.count(), 1); + QCOMPARE(geoCoord->coordinate(), adjustCoord); + QVariantList vertexList = _mapPolyline->path(); + QCOMPARE(vertexList[0].value(), _linePoints[0]); + QCOMPARE(_pathModel->value(0)->coordinate(), _linePoints[0]); + QCOMPARE(vertexList[2].value(), _linePoints[2]); + QCOMPARE(_pathModel->value(2)->coordinate(), _linePoints[2]); + QCOMPARE(vertexList[3].value(), _linePoints[3]); + QCOMPARE(_pathModel->value(3)->coordinate(), _linePoints[3]); + + _mapPolyline->setDirty(false); + _multiSpyPolyline->clearAllSignals(); + _multiSpyModel->clearAllSignals(); + + // Vertex removal testing + + _mapPolyline->removeVertex(1); + QVERIFY(_multiSpyPolyline->checkOnlySignalByMask(pathChangedMask | dirtyChangedMask | countChangedMask)); + QVERIFY(_multiSpyModel->checkOnlySignalByMask(modelDirtyChangedMask | modelCountChangedMask)); + QCOMPARE(_mapPolyline->count(), 3); + vertexList = _mapPolyline->path(); + QCOMPARE(vertexList.count(), 3); + QCOMPARE(_pathModel->count(), 3); + QCOMPARE(vertexList[0].value(), _linePoints[0]); + QCOMPARE(_pathModel->value(0)->coordinate(), _linePoints[0]); + QCOMPARE(vertexList[1].value(), _linePoints[2]); + QCOMPARE(_pathModel->value(1)->coordinate(), _linePoints[2]); + QCOMPARE(vertexList[2].value(), _linePoints[3]); + QCOMPARE(_pathModel->value(2)->coordinate(), _linePoints[3]); + + // Clear testing + + _mapPolyline->clear(); + QVERIFY(_multiSpyPolyline->checkOnlySignalsByMask(pathChangedMask | dirtyChangedMask | countChangedMask | clearedMask)); + QVERIFY(_multiSpyModel->checkOnlySignalsByMask(modelDirtyChangedMask | modelCountChangedMask)); + QVERIFY(_mapPolyline->dirty()); + QVERIFY(_pathModel->dirty()); + QCOMPARE(_mapPolyline->count(), 0); + vertexList = _mapPolyline->path(); + QCOMPARE(vertexList.count(), 0); + QCOMPARE(_pathModel->count(), 0); +} + +#if 0 +void QGCMapPolylineTest::_testKMLLoad(void) +{ + QVERIFY(_mapPolyline->loadKMLFile(QStringLiteral(":/unittest/PolygonGood.kml"))); + + setExpectedMessageBox(QMessageBox::Ok); + QVERIFY(!_mapPolyline->loadKMLFile(QStringLiteral(":/unittest/BadXml.kml"))); + checkExpectedMessageBox(); + + setExpectedMessageBox(QMessageBox::Ok); + QVERIFY(!_mapPolyline->loadKMLFile(QStringLiteral(":/unittest/MissingPolygonNode.kml"))); + checkExpectedMessageBox(); + + setExpectedMessageBox(QMessageBox::Ok); + QVERIFY(!_mapPolyline->loadKMLFile(QStringLiteral(":/unittest/BadCoordinatesNode.kml"))); + checkExpectedMessageBox(); +} +#endif diff --git a/src/MissionManager/QGCMapPolylineTest.h b/src/MissionManager/QGCMapPolylineTest.h new file mode 100644 index 0000000000000000000000000000000000000000..9b02ac54e3d22f1f08ecb594d416e8a9920e4010 --- /dev/null +++ b/src/MissionManager/QGCMapPolylineTest.h @@ -0,0 +1,71 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "UnitTest.h" +#include "MultiSignalSpy.h" +#include "QGCMapPolyline.h" +#include "QmlObjectListModel.h" + +class QGCMapPolylineTest : public UnitTest +{ + Q_OBJECT + +public: + QGCMapPolylineTest(void); + +protected: + void init(void) final; + void cleanup(void) final; + +private slots: + void _testDirty(void); + void _testVertexManipulation(void); +// void _testKMLLoad(void); + +private: + enum { + countChangedIndex = 0, + pathChangedIndex, + dirtyChangedIndex, + clearedIndex, + maxSignalIndex + }; + + enum { + countChangedMask = 1 << countChangedIndex, + pathChangedMask = 1 << pathChangedIndex, + dirtyChangedMask = 1 << dirtyChangedIndex, + clearedMask = 1 << clearedIndex, + }; + + static const size_t _cSignals = maxSignalIndex; + const char* _rgSignals[_cSignals]; + + enum { + modelCountChangedIndex = 0, + modelDirtyChangedIndex, + maxModelSignalIndex + }; + + enum { + modelCountChangedMask = 1 << modelCountChangedIndex, + modelDirtyChangedMask = 1 << modelDirtyChangedIndex, + }; + + static const size_t _cModelSignals = maxModelSignalIndex; + const char* _rgModelSignals[_cModelSignals]; + + MultiSignalSpy* _multiSpyPolyline; + MultiSignalSpy* _multiSpyModel; + QGCMapPolyline* _mapPolyline; + QmlObjectListModel* _pathModel; + QList _linePoints; +}; diff --git a/src/MissionManager/QGCMapPolylineVisuals.qml b/src/MissionManager/QGCMapPolylineVisuals.qml new file mode 100644 index 0000000000000000000000000000000000000000..27f9ed8a787b0fb76effea19e02e4ed687fc69b4 --- /dev/null +++ b/src/MissionManager/QGCMapPolylineVisuals.qml @@ -0,0 +1,287 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtLocation 5.3 +import QtPositioning 5.3 +import QtQuick.Dialogs 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FlightMap 1.0 + +/// QGCmapPolyline map visuals +Item { + id: _root + + property var qgcView ///< QGCView for popping dialogs + property var mapControl ///< Map control to place item in + property var mapPolyline ///< QGCMapPolyline object + property bool interactive: mapPolyline.interactive + property int lineWidth: 3 + property color lineColor: "#be781c" + + + property var _polylineComponent + property var _dragHandlesComponent + property var _splitHandlesComponent + + property real _zorderDragHandle: QGroundControl.zOrderMapItems + 3 // Highest to prevent splitting when items overlap + property real _zorderSplitHandle: QGroundControl.zOrderMapItems + 2 + + function addVisuals() { + _polylineComponent = polylineComponent.createObject(mapControl) + mapControl.addMapItem(_polylineComponent) + } + + function removeVisuals() { + _polylineComponent.destroy() + } + + function addHandles() { + if (!_dragHandlesComponent) { + _dragHandlesComponent = dragHandlesComponent.createObject(mapControl) + _splitHandlesComponent = splitHandlesComponent.createObject(mapControl) + } + } + + function removeHandles() { + if (_dragHandlesComponent) { + _dragHandlesComponent.destroy() + _dragHandlesComponent = undefined + } + if (_splitHandlesComponent) { + _splitHandlesComponent.destroy() + _splitHandlesComponent = undefined + } + } + + /// Calculate the default/initial polyline + function defaultPolylineVertices() { + var x = map.centerViewport.x + (map.centerViewport.width / 2) + var yInset = map.centerViewport.height / 4 + var topPointCoord = map.toCoordinate(Qt.point(x, map.centerViewport.y + yInset), false /* clipToViewPort */) + var bottomPointCoord = map.toCoordinate(Qt.point(x, map.centerViewport.y + map.centerViewport.height - yInset), false /* clipToViewPort */) + return [ topPointCoord, bottomPointCoord ] + } + + /// Add an initial 2 point polyline + function addInitialPolyline() { + if (mapPolyline.count < 2) { + mapPolyline.clear() + var initialVertices = defaultPolylineVertices() + mapPolyline.appendVertex(initialVertices[0]) + mapPolyline.appendVertex(initialVertices[1]) + } + } + + /// Reset polyline back to initial default + function resetPolyline() { + mapPolyline.clear() + addInitialPolyline() + } + + onInteractiveChanged: { + if (interactive) { + addHandles() + } else { + removeHandles() + } + } + + Component.onCompleted: { + addVisuals() + if (interactive) { + addHandles() + } + } + + Component.onDestruction: { + removeVisuals() + removeHandles() + } + + QGCPalette { id: qgcPal } + + QGCFileDialog { + id: kmlLoadDialog + qgcView: _root.qgcView + folder: QGroundControl.settingsManager.appSettings.missionSavePath + title: qsTr("Select KML File") + selectExisting: true + nameFilters: [ qsTr("KML files (*.kml)") ] + + + onAcceptedForLoad: { + mapPolyline.loadKMLFile(file) + close() + } + } + + Component { + id: polylineComponent + + MapPolyline { + line.width: lineWidth + line.color: lineColor + path: mapPolyline.path + } + } + + Component { + id: splitHandleComponent + + MapQuickItem { + id: mapQuickItem + anchorPoint.x: splitHandle.width / 2 + anchorPoint.y: splitHandle.height / 2 + + property int vertexIndex + + sourceItem: Rectangle { + id: splitHandle + width: ScreenTools.defaultFontPixelHeight * 1.5 + height: width + radius: width / 2 + border.color: "white" + color: "transparent" + opacity: .50 + z: _zorderSplitHandle + + QGCLabel { + anchors.horizontalCenter: parent.horizontalCenter + anchors.verticalCenter: parent.verticalCenter + text: "+" + } + + QGCMouseArea { + fillItem: parent + onClicked: mapPolyline.splitSegment(mapQuickItem.vertexIndex) + } + } + } + } + + Component { + id: splitHandlesComponent + + Repeater { + model: mapPolyline.path + + delegate: Item { + property var _splitHandle + property var _vertices: mapPolyline.path + + function _setHandlePosition() { + var nextIndex = index + 1 + var distance = _vertices[index].distanceTo(_vertices[nextIndex]) + var azimuth = _vertices[index].azimuthTo(_vertices[nextIndex]) + _splitHandle.coordinate = _vertices[index].atDistanceAndAzimuth(distance / 2, azimuth) + } + + Component.onCompleted: { + if (index + 1 <= _vertices.length - 1) { + _splitHandle = splitHandleComponent.createObject(mapControl) + _splitHandle.vertexIndex = index + _setHandlePosition() + mapControl.addMapItem(_splitHandle) + } + } + + Component.onDestruction: { + if (_splitHandle) { + _splitHandle.destroy() + } + } + } + } + } + + // Control which is used to drag polygon vertices + Component { + id: dragAreaComponent + + MissionItemIndicatorDrag { + id: dragArea + z: _zorderDragHandle + + property int polylineVertex + + property bool _creationComplete: false + + Component.onCompleted: _creationComplete = true + + onItemCoordinateChanged: { + if (_creationComplete) { + // During component creation some bad coordinate values got through which screws up draw + mapPolyline.adjustVertex(polylineVertex, itemCoordinate) + } + } + + onClicked: mapPolyline.removeVertex(polylineVertex) + } + } + + Component { + id: dragHandleComponent + + MapQuickItem { + id: mapQuickItem + anchorPoint.x: dragHandle.width / 2 + anchorPoint.y: dragHandle.height / 2 + z: _zorderDragHandle + + property int polylineVertex + + sourceItem: Rectangle { + id: dragHandle + width: ScreenTools.defaultFontPixelHeight * 1.5 + height: width + radius: width / 2 + color: "white" + opacity: .90 + } + } + } + + // Add all polygon vertex drag handles to the map + Component { + id: dragHandlesComponent + + Repeater { + model: mapPolyline.pathModel + + delegate: Item { + property var _visuals: [ ] + + Component.onCompleted: { + var dragHandle = dragHandleComponent.createObject(mapControl) + dragHandle.coordinate = Qt.binding(function() { return object.coordinate }) + dragHandle.polylineVertex = Qt.binding(function() { return index }) + mapControl.addMapItem(dragHandle) + var dragArea = dragAreaComponent.createObject(mapControl, { "itemIndicator": dragHandle, "itemCoordinate": object.coordinate }) + dragArea.polylineVertex = Qt.binding(function() { return index }) + _visuals.push(dragHandle) + _visuals.push(dragArea) + } + + Component.onDestruction: { + for (var i=0; i<_visuals.length; i++) { + _visuals[i].destroy() + } + _visuals = [ ] + } + } + } + } +} + diff --git a/src/MissionManager/SectionTest.cc b/src/MissionManager/SectionTest.cc index e177504eaa32509177ba24d861e2414c7d98be5e..f3c72c1e9566d30876ee7b7e816ef0e082e3246e 100644 --- a/src/MissionManager/SectionTest.cc +++ b/src/MissionManager/SectionTest.cc @@ -37,7 +37,7 @@ void SectionTest::init(void) 70.1234567, true, // autoContinue false); // isCurrentItem - _simpleItem = new SimpleMissionItem(_offlineVehicle, missionItem); + _simpleItem = new SimpleMissionItem(_offlineVehicle, true /* editMode */, missionItem); } void SectionTest::cleanup(void) @@ -77,7 +77,7 @@ void SectionTest::_commonScanTest(Section* section) QmlObjectListModel waypointVisualItems; MissionItem waypointItem(0, MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT, 0, 0, 0, 0, 0, 0, 0, true, false); - SimpleMissionItem simpleItem(_offlineVehicle, waypointItem); + SimpleMissionItem simpleItem(_offlineVehicle, true /* editMode */, waypointItem); waypointVisualItems.append(&simpleItem); waypointVisualItems.append(&simpleItem); waypointVisualItems.append(&simpleItem); diff --git a/src/MissionManager/SimpleMissionItem.cc b/src/MissionManager/SimpleMissionItem.cc index 9a672559c268eb12092ffb2f4284b29bcc05e1d2..cc3ee0783eb534c86811db514ce1031909d9056d 100644 --- a/src/MissionManager/SimpleMissionItem.cc +++ b/src/MissionManager/SimpleMissionItem.cc @@ -85,7 +85,7 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, QObject* parent) connect(this, &SimpleMissionItem::cameraSectionChanged, this, &SimpleMissionItem::_updateLastSequenceNumber); } -SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, const MissionItem& missionItem, QObject* parent) +SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, bool editMode, const MissionItem& missionItem, QObject* parent) : VisualMissionItem(vehicle, parent) , _missionItem(missionItem) , _rawEdit(false) @@ -111,11 +111,16 @@ SimpleMissionItem::SimpleMissionItem(Vehicle* vehicle, const MissionItem& missio _altitudeRelativeToHomeFact.setRawValue(true); _isCurrentItem = missionItem.isCurrentItem(); - _setupMetaData(); + // In !editMode we skip some of the intialization to save memory + if (editMode) { + _setupMetaData(); + } _connectSignals(); _updateOptionalSections(); _syncFrameToAltitudeRelativeToHome(); - _rebuildFacts(); + if (editMode) { + _rebuildFacts(); + } } SimpleMissionItem::SimpleMissionItem(const SimpleMissionItem& other, QObject* parent) @@ -349,19 +354,21 @@ QString SimpleMissionItem::commandName(void) const QString SimpleMissionItem::abbreviation() const { if (homePosition()) - return QStringLiteral("H"); + return tr("H"); switch(command()) { - default: - return QString(); case MavlinkQmlSingleton::MAV_CMD_NAV_TAKEOFF: - return QStringLiteral("Takeoff"); + return tr("Takeoff"); case MavlinkQmlSingleton::MAV_CMD_NAV_LAND: - return QStringLiteral("Land"); + return tr("Land"); case MavlinkQmlSingleton::MAV_CMD_NAV_VTOL_TAKEOFF: - return QStringLiteral("VTOL Takeoff"); + return tr("VTOL Takeoff"); case MavlinkQmlSingleton::MAV_CMD_NAV_VTOL_LAND: - return QStringLiteral("VTOL Land"); + return tr("VTOL Land"); + case MavlinkQmlSingleton::MAV_CMD_DO_SET_ROI: + return tr("ROI"); + default: + return QString(); } } @@ -407,9 +414,10 @@ void SimpleMissionItem::_rebuildTextFieldFacts(void) const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); for (int i=1; i<=7; i++) { - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i); + bool showUI; + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI); - if (paramInfo && paramInfo->enumStrings().count() == 0 && !paramInfo->nanUnchanged()) { + if (showUI && paramInfo && paramInfo->enumStrings().count() == 0 && !paramInfo->nanUnchanged()) { Fact* paramFact = rgParamFacts[i-1]; FactMetaData* paramMetaData = rgParamMetaData[i-1]; @@ -451,9 +459,10 @@ void SimpleMissionItem::_rebuildNaNFacts(void) const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); for (int i=1; i<=7; i++) { - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i); + bool showUI; + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI); - if (paramInfo && paramInfo->nanUnchanged()) { + if (showUI && paramInfo && paramInfo->nanUnchanged()) { // Show hide Heading field on waypoint based on vehicle yaw to next waypoint setting. This needs to come from the actual vehicle if it exists // and not _vehicle which is always offline. Vehicle* firmwareVehicle = qgcApp()->toolbox()->multiVehicleManager()->activeVehicle(); @@ -510,9 +519,10 @@ void SimpleMissionItem::_rebuildComboBoxFacts(void) } for (int i=1; i<=7; i++) { - const MissionCmdParamInfo* paramInfo = _commandTree->getUIInfo(_vehicle, command)->getParamInfo(i); + bool showUI; + const MissionCmdParamInfo* paramInfo = _commandTree->getUIInfo(_vehicle, command)->getParamInfo(i, showUI); - if (paramInfo && paramInfo->enumStrings().count() != 0) { + if (showUI && paramInfo && paramInfo->enumStrings().count() != 0) { Fact* paramFact = rgParamFacts[i-1]; FactMetaData* paramMetaData = rgParamMetaData[i-1]; @@ -628,7 +638,8 @@ void SimpleMissionItem::setDefaultsForCommand(void) const MissionCommandUIInfo* uiInfo = _commandTree->getUIInfo(_vehicle, command); if (uiInfo) { for (int i=1; i<=7; i++) { - const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i); + bool showUI; + const MissionCmdParamInfo* paramInfo = uiInfo->getParamInfo(i, showUI); if (paramInfo) { Fact* rgParamFacts[7] = { &_missionItem._param1Fact, &_missionItem._param2Fact, &_missionItem._param3Fact, &_missionItem._param4Fact, &_missionItem._param5Fact, &_missionItem._param6Fact, &_missionItem._param7Fact }; rgParamFacts[paramInfo->param()-1]->setRawValue(paramInfo->defaultValue()); @@ -715,6 +726,11 @@ double SimpleMissionItem::specifiedGimbalYaw(void) return _cameraSection->available() ? _cameraSection->specifiedGimbalYaw() : missionItem().specifiedGimbalYaw(); } +double SimpleMissionItem::specifiedGimbalPitch(void) +{ + return _cameraSection->available() ? _cameraSection->specifiedGimbalPitch() : missionItem().specifiedGimbalPitch(); +} + bool SimpleMissionItem::scanForSections(QmlObjectListModel* visualItems, int scanIndex, Vehicle* vehicle) { bool sectionFound = false; @@ -752,10 +768,12 @@ void SimpleMissionItem::_updateOptionalSections(void) _speedSection->setAvailable(true); } - connect(_cameraSection, &CameraSection::dirtyChanged, this, &SimpleMissionItem::_sectionDirtyChanged); - connect(_cameraSection, &CameraSection::itemCountChanged, this, &SimpleMissionItem::_updateLastSequenceNumber); - connect(_cameraSection, &CameraSection::availableChanged, this, &SimpleMissionItem::specifiedGimbalYawChanged); - connect(_cameraSection, &CameraSection::specifiedGimbalYawChanged, this, &SimpleMissionItem::specifiedGimbalYawChanged); + connect(_cameraSection, &CameraSection::dirtyChanged, this, &SimpleMissionItem::_sectionDirtyChanged); + connect(_cameraSection, &CameraSection::itemCountChanged, this, &SimpleMissionItem::_updateLastSequenceNumber); + connect(_cameraSection, &CameraSection::availableChanged, this, &SimpleMissionItem::specifiedGimbalYawChanged); + connect(_cameraSection, &CameraSection::availableChanged, this, &SimpleMissionItem::specifiedGimbalPitchChanged); + connect(_cameraSection, &CameraSection::specifiedGimbalPitchChanged, this, &SimpleMissionItem::specifiedGimbalPitchChanged); + connect(_cameraSection, &CameraSection::specifiedGimbalYawChanged, this, &SimpleMissionItem::specifiedGimbalYawChanged); connect(_speedSection, &SpeedSection::dirtyChanged, this, &SimpleMissionItem::_sectionDirtyChanged); connect(_speedSection, &SpeedSection::itemCountChanged, this, &SimpleMissionItem::_updateLastSequenceNumber); @@ -811,3 +829,20 @@ void SimpleMissionItem::applyNewAltitude(double newAltitude) } } } + +void SimpleMissionItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus) +{ + // If user has not already set speed/gimbal, set defaults from previous items. + VisualMissionItem::setMissionFlightStatus(missionFlightStatus); + if (_speedSection->available() && !_speedSection->specifyFlightSpeed() && !qFuzzyCompare(_speedSection->flightSpeed()->rawValue().toDouble(), missionFlightStatus.vehicleSpeed)) { + _speedSection->flightSpeed()->setRawValue(missionFlightStatus.vehicleSpeed); + } + if (_cameraSection->available() && !_cameraSection->specifyGimbal()) { + if (!qIsNaN(missionFlightStatus.gimbalYaw) && !qFuzzyCompare(_cameraSection->gimbalYaw()->rawValue().toDouble(), missionFlightStatus.gimbalYaw)) { + _cameraSection->gimbalYaw()->setRawValue(missionFlightStatus.gimbalYaw); + } + if (!qIsNaN(missionFlightStatus.gimbalPitch) && !qFuzzyCompare(_cameraSection->gimbalPitch()->rawValue().toDouble(), missionFlightStatus.gimbalPitch)) { + _cameraSection->gimbalPitch()->setRawValue(missionFlightStatus.gimbalPitch); + } + } +} diff --git a/src/MissionManager/SimpleMissionItem.h b/src/MissionManager/SimpleMissionItem.h index 2e0e0cafe66dcfc82e731daa25c8873d36cf4c43..c316804de0fa54cb67df9ba26f01bebc95ff434a 100644 --- a/src/MissionManager/SimpleMissionItem.h +++ b/src/MissionManager/SimpleMissionItem.h @@ -24,7 +24,7 @@ class SimpleMissionItem : public VisualMissionItem public: SimpleMissionItem(Vehicle* vehicle, QObject* parent = NULL); - SimpleMissionItem(Vehicle* vehicle, const MissionItem& missionItem, QObject* parent = NULL); + SimpleMissionItem(Vehicle* vehicle, bool editMode, const MissionItem& missionItem, QObject* parent = NULL); SimpleMissionItem(const SimpleMissionItem& other, QObject* parent = NULL); ~SimpleMissionItem(); @@ -102,9 +102,11 @@ public: int sequenceNumber (void) const final { return _missionItem.sequenceNumber(); } double specifiedFlightSpeed (void) final; double specifiedGimbalYaw (void) final; + double specifiedGimbalPitch (void) final; QString mapVisualQML (void) const final { return QStringLiteral("SimpleItemMapVisual.qml"); } void appendMissionItems (QList& items, QObject* missionItemParent) final; void applyNewAltitude (double newAltitude) final; + void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; bool coordinateHasRelativeAltitude (void) const final { return _missionItem.relativeAltitude(); } bool exitCoordinateHasRelativeAltitude (void) const final { return coordinateHasRelativeAltitude(); } diff --git a/src/MissionManager/SimpleMissionItemTest.cc b/src/MissionManager/SimpleMissionItemTest.cc index 34ae7f19e58d21d188e568d68dadfd0a23a39e5e..453987813328fd0057b5894ac65c316bef41ed88 100644 --- a/src/MissionManager/SimpleMissionItemTest.cc +++ b/src/MissionManager/SimpleMissionItemTest.cc @@ -100,7 +100,7 @@ void SimpleMissionItemTest::init(void) 70.1234567, true, // autoContinue false); // isCurrentItem - _simpleItem = new SimpleMissionItem(_offlineVehicle, missionItem); + _simpleItem = new SimpleMissionItem(_offlineVehicle, true /* editMode */, missionItem); // It's important top check that the right signals are emitted at the right time since that drives ui change. // It's also important to check that things are not being over-signalled when they should not be, since that can lead @@ -139,7 +139,7 @@ void SimpleMissionItemTest::_testEditorFacts(void) 70.1234567, true, // autoContinue false); // isCurrentItem - SimpleMissionItem simpleMissionItem(vehicle, missionItem); + SimpleMissionItem simpleMissionItem(vehicle, true /* editMode */, missionItem); // Validate that the fact values are correctly returned diff --git a/src/MissionManager/SpeedSection.cc b/src/MissionManager/SpeedSection.cc index 6f465a3dca0ab9a9b5afccf64a72f988d353480d..90e49b5b2368442ecbf5b47d3f305114dd9667ea 100644 --- a/src/MissionManager/SpeedSection.cc +++ b/src/MissionManager/SpeedSection.cc @@ -39,7 +39,7 @@ SpeedSection::SpeedSection(Vehicle* vehicle, QObject* parent) _flightSpeedFact.setRawValue(flightSpeed); connect(this, &SpeedSection::specifyFlightSpeedChanged, this, &SpeedSection::settingsSpecifiedChanged); - connect(&_flightSpeedFact, &Fact::valueChanged, this, &SpeedSection::_setDirty); + connect(&_flightSpeedFact, &Fact::valueChanged, this, &SpeedSection::_flightSpeedChanged); connect(this, &SpeedSection::specifyFlightSpeedChanged, this, &SpeedSection::_updateSpecifiedFlightSpeed); connect(&_flightSpeedFact, &Fact::valueChanged, this, &SpeedSection::_updateSpecifiedFlightSpeed); @@ -60,11 +60,6 @@ void SpeedSection::setAvailable(bool available) } } -void SpeedSection::_setDirty(void) -{ - setDirty(true); -} - void SpeedSection::setDirty(bool dirty) { if (_dirty != dirty) { @@ -146,6 +141,16 @@ double SpeedSection::specifiedFlightSpeed(void) const void SpeedSection::_updateSpecifiedFlightSpeed(void) { - emit specifiedFlightSpeedChanged(specifiedFlightSpeed()); + if (_specifyFlightSpeed) { + emit specifiedFlightSpeedChanged(specifiedFlightSpeed()); + } } +void SpeedSection::_flightSpeedChanged(void) +{ + // We only set the dirty bit if specify flight speed it set. This allows us to change defaults for flight speed + // without affecting dirty. + if (_specifyFlightSpeed) { + setDirty(true); + } +} diff --git a/src/MissionManager/SpeedSection.h b/src/MissionManager/SpeedSection.h index 1b37b8f9f21facd47d15497cb03489639c44901b..ad0c66fde1763ec118ddaa6d52633a8e0ce4722d 100644 --- a/src/MissionManager/SpeedSection.h +++ b/src/MissionManager/SpeedSection.h @@ -46,8 +46,8 @@ signals: void specifiedFlightSpeedChanged (double flightSpeed); private slots: - void _setDirty (void); void _updateSpecifiedFlightSpeed(void); + void _flightSpeedChanged (void); private: bool _available; diff --git a/src/MissionManager/SpeedSectionTest.cc b/src/MissionManager/SpeedSectionTest.cc index eeed8ee2e3b40da65be30d65ab4d13c119ccb7ca..b62db1c56fb6f7ddb48fc9b4b63550fffa1daff8 100644 --- a/src/MissionManager/SpeedSectionTest.cc +++ b/src/MissionManager/SpeedSectionTest.cc @@ -73,7 +73,18 @@ void SpeedSectionTest::_testDirty(void) QCOMPARE(_speedSection->dirty(), false); _spySection->clearAllSignals(); - // Check the remaining items that should set dirty bit + // Flight speed change should only signal if specifyFlightSpeed is set + + _speedSection->setSpecifyFlightSpeed(false); + _speedSection->setDirty(false); + _spySection->clearAllSignals(); + _speedSection->flightSpeed()->setRawValue(_speedSection->flightSpeed()->rawValue().toDouble() + 1); + QVERIFY(_spySection->checkNoSignalByMask(dirtyChangedMask)); + QCOMPARE(_speedSection->dirty(), false); + + _speedSection->setSpecifyFlightSpeed(true); + _speedSection->setDirty(false); + _spySection->clearAllSignals(); _speedSection->flightSpeed()->setRawValue(_speedSection->flightSpeed()->rawValue().toDouble() + 1); QVERIFY(_spySection->checkSignalByMask(dirtyChangedMask)); QCOMPARE(_spySection->pullBoolFromSignalIndex(dirtyChangedIndex), true); @@ -123,7 +134,7 @@ void SpeedSectionTest::_checkAvailable(void) 70.1234567, true, // autoContinue false); // isCurrentItem - SimpleMissionItem* item = new SimpleMissionItem(_offlineVehicle, missionItem); + SimpleMissionItem* item = new SimpleMissionItem(_offlineVehicle, true /* editMode */, missionItem); QVERIFY(item->speedSection()); QCOMPARE(item->speedSection()->available(), false); } @@ -182,7 +193,7 @@ void SpeedSectionTest::_testScanForSection(void) double flightSpeed = 10.123456; MissionItem validSpeedItem(0, MAV_CMD_DO_CHANGE_SPEED, MAV_FRAME_MISSION, _offlineVehicle->multiRotor() ? 1 : 0, flightSpeed, -1, 0, 0, 0, 0, true, false); - SimpleMissionItem simpleItem(_offlineVehicle, validSpeedItem); + SimpleMissionItem simpleItem(_offlineVehicle, true /* editMode */, validSpeedItem); MissionItem& simpleMissionItem = simpleItem.missionItem(); visualItems.append(&simpleItem); scanIndex = 0; @@ -254,7 +265,7 @@ void SpeedSectionTest::_testScanForSection(void) // Valid item in wrong position QmlObjectListModel waypointVisualItems; MissionItem waypointMissionItem(0, MAV_CMD_NAV_WAYPOINT, MAV_FRAME_GLOBAL_RELATIVE_ALT, 0, 0, 0, 0, 0, 0, 0, true, false); - SimpleMissionItem simpleWaypointItem(_offlineVehicle, waypointMissionItem); + SimpleMissionItem simpleWaypointItem(_offlineVehicle, true /* editMode */, waypointMissionItem); simpleMissionItem = validSpeedItem; visualItems.append(&simpleWaypointItem); visualItems.append(&simpleMissionItem); @@ -264,3 +275,18 @@ void SpeedSectionTest::_testScanForSection(void) visualItems.clear(); scanIndex = 0; } + +void SpeedSectionTest::_testSpecifiedFlightSpeedChanged(void) +{ + // specifiedFlightSpeedChanged SHOULD NOT signal if flight speed is changed when specifyFlightSpeed IS NOT set + _speedSection->setSpecifyFlightSpeed(false); + _spySpeed->clearAllSignals(); + _speedSection->flightSpeed()->setRawValue(_speedSection->flightSpeed()->rawValue().toDouble() + 1); + QVERIFY(_spySpeed->checkNoSignalByMask(specifiedFlightSpeedChangedMask)); + + // specifiedFlightSpeedChanged SHOULD signal if flight speed is changed when specifyFlightSpeed IS set + _speedSection->setSpecifyFlightSpeed(true); + _spySpeed->clearAllSignals(); + _speedSection->flightSpeed()->setRawValue(_speedSection->flightSpeed()->rawValue().toDouble() + 1); + QVERIFY(_spySpeed->checkSignalByMask(specifiedFlightSpeedChangedMask)); +} diff --git a/src/MissionManager/SpeedSectionTest.h b/src/MissionManager/SpeedSectionTest.h index 406a86d672be805775fadc989a874328c1cad6fc..fe61a6ec72b4450dd7191f5566fb46a4112f7890 100644 --- a/src/MissionManager/SpeedSectionTest.h +++ b/src/MissionManager/SpeedSectionTest.h @@ -24,12 +24,13 @@ public: void cleanup(void) override; private slots: - void _testDirty(void); - void _testSettingsAvailable(void); - void _checkAvailable(void); - void _testItemCount(void); - void _testAppendSectionItems(void); - void _testScanForSection(void); + void _testDirty (void); + void _testSettingsAvailable (void); + void _checkAvailable (void); + void _testItemCount (void); + void _testAppendSectionItems (void); + void _testScanForSection (void); + void _testSpecifiedFlightSpeedChanged (void); private: void _createSpy(SpeedSection* speedSection, MultiSignalSpy** speedSpy); diff --git a/src/MissionManager/StructureScan.SettingsGroup.json b/src/MissionManager/StructureScan.SettingsGroup.json index 8439fa69dccd3d144ed7cf526707fc641b4c8c8a..9c3beebabe537b31799f6365c8cee58cd2697ffc 100644 --- a/src/MissionManager/StructureScan.SettingsGroup.json +++ b/src/MissionManager/StructureScan.SettingsGroup.json @@ -1,4 +1,24 @@ [ +{ + "name": "GimbalPitch", + "shortDescription": "Gimbal pitch rotation.", + "type": "double", + "units": "gimbal-degrees", + "min": -90, + "max": 0, + "decimalPlaces": 0, + "defaultValue": 0 +}, +{ + "name": "GimbalYaw", + "shortDescription": "Gimbal yaw rotation.", + "type": "double", + "units": "deg", + "min": -180.0, + "max": 180.0, + "decimalPlaces": 0, + "defaultValue": 90 +}, { "name": "Altitude", "shortDescription": "Altitude for the bottom layer of the structure scan.", diff --git a/src/MissionManager/StructureScanComplexItem.cc b/src/MissionManager/StructureScanComplexItem.cc index c03379bf76642c95b6caeb6e325dbe738a893c3c..3942ef5cc4aadcf6ff0fe9731ccca47774c05ca1 100644 --- a/src/MissionManager/StructureScanComplexItem.cc +++ b/src/MissionManager/StructureScanComplexItem.cc @@ -21,12 +21,13 @@ QGC_LOGGING_CATEGORY(StructureScanComplexItemLog, "StructureScanComplexItemLog") -const char* StructureScanComplexItem::jsonComplexItemTypeValue = "StructureScan"; const char* StructureScanComplexItem::_altitudeFactName = "Altitude"; +const char* StructureScanComplexItem::_structureHeightFactName = "StructureHeight"; const char* StructureScanComplexItem::_layersFactName = "Layers"; + +const char* StructureScanComplexItem::jsonComplexItemTypeValue = "StructureScan"; const char* StructureScanComplexItem::_jsonCameraCalcKey = "CameraCalc"; const char* StructureScanComplexItem::_jsonAltitudeRelativeKey = "altitudeRelative"; -const char* StructureScanComplexItem::_jsonYawVehicleToStructureKey = "yawVehicleToStructure"; QMap StructureScanComplexItem::_metaDataMap; @@ -41,7 +42,6 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa , _cameraShots (0) , _cameraMinTriggerInterval (0) , _cameraCalc (vehicle) - , _yawVehicleToStructure (false) , _altitudeFact (0, _altitudeFactName, FactMetaData::valueTypeDouble) , _layersFact (0, _layersFactName, FactMetaData::valueTypeUint32) { @@ -59,12 +59,16 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa _altitudeFact.setRawValue(qgcApp()->toolbox()->settingsManager()->appSettings()->defaultMissionItemAltitude()->rawValue()); - connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty); - connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty); + connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty); + connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_setDirty); + + connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcLayerInfo); + connect(&_structureHeightFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcLayerInfo); + connect(_cameraCalc.adjustedFootprintFrontal(), &Fact::valueChanged, this, &StructureScanComplexItem::_recalcLayerInfo); - connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::_setDirty); - connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::coordinateHasRelativeAltitudeChanged); - connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::exitCoordinateHasRelativeAltitudeChanged); + connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::_setDirty); + connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::coordinateHasRelativeAltitudeChanged); + connect(this, &StructureScanComplexItem::altitudeRelativeChanged, this, &StructureScanComplexItem::exitCoordinateHasRelativeAltitudeChanged); connect(&_altitudeFact, &Fact::valueChanged, this, &StructureScanComplexItem::_updateCoordinateAltitudes); @@ -74,18 +78,20 @@ StructureScanComplexItem::StructureScanComplexItem(Vehicle* vehicle, QObject* pa connect(&_flightPolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_flightPathChanged); - connect(_cameraCalc.distanceToSurface(), &Fact::valueChanged, this, &StructureScanComplexItem::_rebuildFlightPolygon); + connect(_cameraCalc.distanceToSurface(), &Fact::valueChanged, this, &StructureScanComplexItem::_rebuildFlightPolygon); connect(&_flightPolygon, &QGCMapPolygon::pathChanged, this, &StructureScanComplexItem::_recalcCameraShots); connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &StructureScanComplexItem::_recalcCameraShots); connect(&_layersFact, &Fact::valueChanged, this, &StructureScanComplexItem::_recalcCameraShots); + + _recalcLayerInfo(); } void StructureScanComplexItem::_setScanDistance(double scanDistance) { if (!qFuzzyCompare(_scanDistance, scanDistance)) { _scanDistance = scanDistance; - emit complexDistanceChanged(_scanDistance); + emit complexDistanceChanged(); } } @@ -114,8 +120,10 @@ void StructureScanComplexItem::_polygonCountChanged(int count) int StructureScanComplexItem::lastSequenceNumber(void) const { return _sequenceNumber + - ((_flightPolygon.count() + 1) * _layersFact.rawValue().toInt()) + // 1 waypoint for each polygon vertex + 1 to go back to first polygon vertex - 1; // Gimbal yaw command + (_layersFact.rawValue().toInt() * + ((_flightPolygon.count() + 1) + // 1 waypoint for each polygon vertex + 1 to go back to first polygon vertex for each layer + 2)) + // Camera trigger start/stop for each layer + 2; // ROI_WPNEXT_OFFSET and ROI_NONE commands } void StructureScanComplexItem::setDirty(bool dirty) @@ -131,14 +139,14 @@ void StructureScanComplexItem::save(QJsonArray& missionItems) QJsonObject saveObject; // Header - saveObject[JsonHelper::jsonVersionKey] = 1; + saveObject[JsonHelper::jsonVersionKey] = 2; saveObject[VisualMissionItem::jsonTypeKey] = VisualMissionItem::jsonTypeComplexItemValue; saveObject[ComplexMissionItem::jsonComplexItemTypeKey] = jsonComplexItemTypeValue; saveObject[_altitudeFactName] = _altitudeFact.rawValue().toDouble(); + saveObject[_structureHeightFactName] = _structureHeightFact.rawValue().toDouble(); saveObject[_jsonAltitudeRelativeKey] = _altitudeRelative; saveObject[_layersFactName] = _layersFact.rawValue().toDouble(); - saveObject[_jsonYawVehicleToStructureKey] = _yawVehicleToStructure; QJsonObject cameraCalcObject; _cameraCalc.save(cameraCalcObject); @@ -166,10 +174,10 @@ bool StructureScanComplexItem::load(const QJsonObject& complexObject, int sequen { ComplexMissionItem::jsonComplexItemTypeKey, QJsonValue::String, true }, { QGCMapPolygon::jsonPolygonKey, QJsonValue::Array, true }, { _altitudeFactName, QJsonValue::Double, true }, - { _jsonAltitudeRelativeKey, QJsonValue::Bool, false }, + { _structureHeightFactName, QJsonValue::Double, true }, + { _jsonAltitudeRelativeKey, QJsonValue::Bool, true }, { _layersFactName, QJsonValue::Double, true }, { _jsonCameraCalcKey, QJsonValue::Object, true }, - { _jsonYawVehicleToStructureKey, QJsonValue::Bool, true }, }; if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { return false; @@ -185,21 +193,22 @@ bool StructureScanComplexItem::load(const QJsonObject& complexObject, int sequen } int version = complexObject[JsonHelper::jsonVersionKey].toInt(); - if (version != 1) { - errorString = tr("Version %1 not supported").arg(version); + if (version != 2) { + errorString = tr("%1 complex item version %2 not supported").arg(jsonComplexItemTypeValue).arg(version); return false; } setSequenceNumber(sequenceNumber); + // Load CameraCalc first since it will trigger camera name change which will trounce gimbal angles + if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) { + return false; + } + _altitudeFact.setRawValue (complexObject[_altitudeFactName].toDouble()); _layersFact.setRawValue (complexObject[_layersFactName].toDouble()); _altitudeRelative = complexObject[_jsonAltitudeRelativeKey].toBool(true); - _yawVehicleToStructure = complexObject[_jsonYawVehicleToStructureKey].toBool(true); - if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) { - return false; - } if (!_structurePolygon.loadFromJson(complexObject, true /* required */, errorString)) { _structurePolygon.clear(); return false; @@ -241,37 +250,22 @@ void StructureScanComplexItem::appendMissionItems(QList& items, QO int seqNum = _sequenceNumber; double baseAltitude = _altitudeFact.rawValue().toDouble(); - if (_yawVehicleToStructure) { - MissionItem* item = new MissionItem(seqNum++, - MAV_CMD_CONDITION_YAW, - MAV_FRAME_MISSION, - 90.0, // Target angle - 0, // Use default turn rate - 1, // Clockwise turn - 0, // Absolute angle specified - 0, 0, 0, // param 5-7 not used - true, // autoContinue - false, // isCurrentItem - missionItemParent); - items.append(item); - } else { - MissionItem* item = new MissionItem(seqNum++, - MAV_CMD_DO_MOUNT_CONTROL, - MAV_FRAME_MISSION, - 0, // Gimbal pitch - 0, // Gimbal roll - 90, // Gimbal yaw - 0, 0, 0, // param 4-6 not used - MAV_MOUNT_MODE_MAVLINK_TARGETING, - true, // autoContinue - false, // isCurrentItem - missionItemParent); - items.append(item); - } + MissionItem* item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_ROI_WPNEXT_OFFSET, + MAV_FRAME_MISSION, + 0, 0, 0, 0, // param 1-4 not used + 0, 0, // Pitch and Roll stay in standard orientation + 90, // 90 degreee yaw offset to point to structure + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); for (int layer=0; layer<_layersFact.rawValue().toInt(); layer++) { bool addTriggerStart = true; - double layerAltitude = baseAltitude + (layer * _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); + // baseAltitude is the bottom of the first layer. Hence we need to move up half the distance of the camera footprint to center the camera + // within the layer. + double layerAltitude = baseAltitude + (_cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble() / 2.0) + (layer * _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); for (int i=0; i<_flightPolygon.count(); i++) { QGeoCoordinate vertexCoord = _flightPolygon.vertexCoordinate(i); @@ -336,6 +330,15 @@ void StructureScanComplexItem::appendMissionItems(QList& items, QO missionItemParent); items.append(item); } + + item = new MissionItem(seqNum++, + MAV_CMD_DO_SET_ROI_NONE, + MAV_FRAME_MISSION, + 0, 0, 0,0, 0, 0, 0, // param 1-7 not used + true, // autoContinue + false, // isCurrentItem + missionItemParent); + items.append(item); } int StructureScanComplexItem::cameraShots(void) const @@ -433,3 +436,28 @@ void StructureScanComplexItem::_recalcCameraShots(void) int cameraShots = distance / _cameraCalc.adjustedFootprintSide()->rawValue().toDouble(); _setCameraShots(cameraShots * _layersFact.rawValue().toInt()); } + +void StructureScanComplexItem::setAltitudeRelative(bool altitudeRelative) +{ + if (altitudeRelative != _altitudeRelative) { + _altitudeRelative = altitudeRelative; + emit altitudeRelativeChanged(altitudeRelative); + } +} + +void StructureScanComplexItem::_recalcLayerInfo(void) +{ + if (_cameraCalc.isManualCamera()) { + // Structure height is calculated from layer count, layer height. + _structureHeightFact.setSendValueChangedSignals(false); + _structureHeightFact.setRawValue(_layersFact.rawValue().toInt() * _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble()); + _structureHeightFact.clearDeferredValueChangeSignal(); + _structureHeightFact.setSendValueChangedSignals(true); + } else { + // Layer count is calculated from structure and layer heights + _layersFact.setSendValueChangedSignals(false); + _layersFact.setRawValue(qCeil(_structureHeightFact.rawValue().toDouble() / _cameraCalc.adjustedFootprintFrontal()->rawValue().toDouble())); + _layersFact.clearDeferredValueChangeSignal(); + _layersFact.setSendValueChangedSignals(true); + } +} diff --git a/src/MissionManager/StructureScanComplexItem.h b/src/MissionManager/StructureScanComplexItem.h index 1b3374d98e793b5beef7d5d0e3bfbc842cdc9afb..8ebffef5139507c9ce314d745cb550b53b315e4b 100644 --- a/src/MissionManager/StructureScanComplexItem.h +++ b/src/MissionManager/StructureScanComplexItem.h @@ -27,25 +27,29 @@ class StructureScanComplexItem : public ComplexMissionItem public: StructureScanComplexItem(Vehicle* vehicle, QObject* parent = NULL); - Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) - Q_PROPERTY(Fact* altitude READ altitude CONSTANT) - Q_PROPERTY(Fact* layers READ layers CONSTANT) - Q_PROPERTY(bool altitudeRelative MEMBER _altitudeRelative NOTIFY altitudeRelativeChanged) - Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) - Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) - Q_PROPERTY(double cameraMinTriggerInterval MEMBER _cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged) - Q_PROPERTY(QGCMapPolygon* structurePolygon READ structurePolygon CONSTANT) - Q_PROPERTY(QGCMapPolygon* flightPolygon READ flightPolygon CONSTANT) - Q_PROPERTY(bool yawVehicleToStructure MEMBER _yawVehicleToStructure NOTIFY yawVehicleToStructureChanged) ///< true: vehicle yaws to point to structure, false: gimbal yaws to point to structure + Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) + Q_PROPERTY(Fact* altitude READ altitude CONSTANT) + Q_PROPERTY(Fact* structureHeight READ structureHeight CONSTANT) + Q_PROPERTY(Fact* layers READ layers CONSTANT) + Q_PROPERTY(bool altitudeRelative READ altitudeRelative WRITE setAltitudeRelative NOTIFY altitudeRelativeChanged) + Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) + Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) + Q_PROPERTY(double cameraMinTriggerInterval MEMBER _cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged) + Q_PROPERTY(QGCMapPolygon* structurePolygon READ structurePolygon CONSTANT) + Q_PROPERTY(QGCMapPolygon* flightPolygon READ flightPolygon CONSTANT) CameraCalc* cameraCalc (void) { return &_cameraCalc; } Fact* altitude (void) { return &_altitudeFact; } + Fact* structureHeight (void) { return &_structureHeightFact; } Fact* layers (void) { return &_layersFact; } - int cameraShots (void) const; - double timeBetweenShots(void); - QGCMapPolygon* structurePolygon(void) { return &_structurePolygon; } - QGCMapPolygon* flightPolygon (void) { return &_flightPolygon; } + bool altitudeRelative (void) const { return _altitudeRelative; } + int cameraShots (void) const; + double timeBetweenShots (void); + QGCMapPolygon* structurePolygon (void) { return &_structurePolygon; } + QGCMapPolygon* flightPolygon (void) { return &_flightPolygon; } + + void setAltitudeRelative (bool altitudeRelative); Q_INVOKABLE void rotateEntryPoint(void); @@ -72,6 +76,7 @@ public: int sequenceNumber (void) const final { return _sequenceNumber; } double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } void appendMissionItems (QList& items, QObject* missionItemParent) final; void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; void applyNewAltitude (double newAltitude) final; @@ -92,17 +97,17 @@ signals: void timeBetweenShotsChanged (void); void cameraMinTriggerIntervalChanged(double cameraMinTriggerInterval); void altitudeRelativeChanged (bool altitudeRelative); - void yawVehicleToStructureChanged (bool yawVehicleToStructure); private slots: void _setDirty(void); - void _polygonDirtyChanged(bool dirty); - void _polygonCountChanged(int count); - void _flightPathChanged(void); - void _clearInternal(void); - void _updateCoordinateAltitudes(void); - void _rebuildFlightPolygon(void); - void _recalcCameraShots(void); + void _polygonDirtyChanged (bool dirty); + void _polygonCountChanged (int count); + void _flightPathChanged (void); + void _clearInternal (void); + void _updateCoordinateAltitudes (void); + void _rebuildFlightPolygon (void); + void _recalcCameraShots (void); + void _recalcLayerInfo (void); private: void _setExitCoordinate(const QGeoCoordinate& coordinate); @@ -124,19 +129,21 @@ private: double _cameraMinTriggerInterval; double _cruiseSpeed; CameraCalc _cameraCalc; - bool _yawVehicleToStructure; static QMap _metaDataMap; Fact _altitudeFact; + Fact _structureHeightFact; Fact _layersFact; static const char* _altitudeFactName; + static const char* _structureHeightFactName; static const char* _layersFactName; static const char* _jsonCameraCalcKey; static const char* _jsonAltitudeRelativeKey; - static const char* _jsonYawVehicleToStructureKey; + + friend class StructureScanComplexItemTest; }; #endif diff --git a/src/MissionManager/StructureScanComplexItemTest.cc b/src/MissionManager/StructureScanComplexItemTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..3a2a4081f5e5cc92294ce09d49df3ed3c706ce12 --- /dev/null +++ b/src/MissionManager/StructureScanComplexItemTest.cc @@ -0,0 +1,139 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "StructureScanComplexItemTest.h" +#include "QGCApplication.h" + +StructureScanComplexItemTest::StructureScanComplexItemTest(void) + : _offlineVehicle(NULL) +{ + _polyPoints << QGeoCoordinate(47.633550640000003, -122.08982199) << QGeoCoordinate(47.634129020000003, -122.08887249) << + QGeoCoordinate(47.633619320000001, -122.08811074) << QGeoCoordinate(47.633189139999999, -122.08900124); +} + +void StructureScanComplexItemTest::init(void) +{ + UnitTest::init(); + + _rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); + + _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); + _structureScanItem = new StructureScanComplexItem(_offlineVehicle, this); + _structureScanItem->setDirty(false); + + _multiSpy = new MultiSignalSpy(); + Q_CHECK_PTR(_multiSpy); + QCOMPARE(_multiSpy->init(_structureScanItem, _rgSignals, _cSignals), true); +} + +void StructureScanComplexItemTest::cleanup(void) +{ + delete _structureScanItem; + delete _offlineVehicle; +} + +void StructureScanComplexItemTest::_testDirty(void) +{ + QVERIFY(!_structureScanItem->dirty()); + _structureScanItem->setDirty(false); + QVERIFY(!_structureScanItem->dirty()); + QVERIFY(_multiSpy->checkNoSignals()); + + _structureScanItem->setDirty(true); + QVERIFY(_structureScanItem->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _multiSpy->clearAllSignals(); + + _structureScanItem->setDirty(false); + QVERIFY(!_structureScanItem->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(!_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _multiSpy->clearAllSignals(); + + // These facts should set dirty when changed + QList rgFacts; + rgFacts << _structureScanItem->altitude() << _structureScanItem->layers(); + foreach(Fact* fact, rgFacts) { + qDebug() << fact->name(); + QVERIFY(!_structureScanItem->dirty()); + if (fact->typeIsBool()) { + fact->setRawValue(!fact->rawValue().toBool()); + } else { + fact->setRawValue(fact->rawValue().toDouble() + 1); + } + QVERIFY(_multiSpy->checkSignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _structureScanItem->setDirty(false); + _multiSpy->clearAllSignals(); + } + rgFacts.clear(); + + QVERIFY(!_structureScanItem->dirty()); + _structureScanItem->setAltitudeRelative(!_structureScanItem->altitudeRelative()); + QVERIFY(_multiSpy->checkSignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _structureScanItem->setDirty(false); + _multiSpy->clearAllSignals(); +} + +void StructureScanComplexItemTest::_initItem(void) +{ + QGCMapPolygon* mapPolygon = _structureScanItem->structurePolygon(); + + for (int i=0; i<_polyPoints.count(); i++) { + QGeoCoordinate& vertex = _polyPoints[i]; + mapPolygon->appendVertex(vertex); + } + + _structureScanItem->cameraCalc()->setCameraName(CameraCalc::manualCameraName()); + _structureScanItem->layers()->setCookedValue(2); + _structureScanItem->setDirty(false); + + _validateItem(_structureScanItem); +} + +void StructureScanComplexItemTest::_validateItem(StructureScanComplexItem* item) +{ + QGCMapPolygon* mapPolygon = item->structurePolygon(); + + for (int i=0; i<_polyPoints.count(); i++) { + QGeoCoordinate& expectedVertex = _polyPoints[i]; + QGeoCoordinate actualVertex = mapPolygon->vertexCoordinate(i); + QCOMPARE(expectedVertex, actualVertex); + } + + QCOMPARE(_structureScanItem->cameraCalc()->cameraName() , CameraCalc::manualCameraName()); + QCOMPARE(item->layers()->cookedValue().toInt(), 2); +} + +void StructureScanComplexItemTest::_testSaveLoad(void) +{ + _initItem(); + + QJsonArray items; + _structureScanItem->save(items); + + QString errorString; + StructureScanComplexItem* newItem = new StructureScanComplexItem(_offlineVehicle, this); + QVERIFY(newItem->load(items[0].toObject(), 10, errorString)); + QVERIFY(errorString.isEmpty()); + _validateItem(newItem); + newItem->deleteLater(); +} + +void StructureScanComplexItemTest::_testItemCount(void) +{ + QList items; + + _initItem(); + _structureScanItem->appendMissionItems(items, this); + QCOMPARE(items.count(), _structureScanItem->lastSequenceNumber()); + +} diff --git a/src/MissionManager/StructureScanComplexItemTest.h b/src/MissionManager/StructureScanComplexItemTest.h new file mode 100644 index 0000000000000000000000000000000000000000..06f5e1b1a3ac2de335b7c9cee033547877749266 --- /dev/null +++ b/src/MissionManager/StructureScanComplexItemTest.h @@ -0,0 +1,52 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "UnitTest.h" +#include "MultiSignalSpy.h" +#include "StructureScanComplexItem.h" + +class StructureScanComplexItemTest : public UnitTest +{ + Q_OBJECT + +public: + StructureScanComplexItemTest(void); + +protected: + void init(void) final; + void cleanup(void) final; + +private slots: + void _testDirty(void); + void _testSaveLoad(void); + void _testItemCount(void); + +private: + void _initItem(void); + void _validateItem(StructureScanComplexItem* item); + + enum { + dirtyChangedIndex, + maxSignalIndex + }; + + enum { + dirtyChangedMask = 1 << dirtyChangedIndex + }; + + static const size_t _cSignals = maxSignalIndex; + const char* _rgSignals[_cSignals]; + + Vehicle* _offlineVehicle; + MultiSignalSpy* _multiSpy; + StructureScanComplexItem* _structureScanItem; + QList _polyPoints; +}; diff --git a/src/MissionManager/SurveyMissionItem.cc b/src/MissionManager/SurveyMissionItem.cc index 7dce0aefb9bf38bf7a0fd149ab819aa06f236694..1e8b5e9f4590bde163effb055a5212090f76e4b0 100644 --- a/src/MissionManager/SurveyMissionItem.cc +++ b/src/MissionManager/SurveyMissionItem.cc @@ -159,7 +159,7 @@ void SurveyMissionItem::_setSurveyDistance(double surveyDistance) { if (!qFuzzyCompare(_surveyDistance, surveyDistance)) { _surveyDistance = surveyDistance; - emit complexDistanceChanged(_surveyDistance); + emit complexDistanceChanged(); } } @@ -638,6 +638,28 @@ void SurveyMissionItem::_adjustTransectsToEntryPointLocation(QList>& transectSegments) +{ + int missionCommandCount= 0; + for (int i=0; i& transectSegment = transectSegments[i]; + + missionCommandCount += transectSegment.count(); // This accounts for all waypoints + if (_hoverAndCaptureEnabled()) { + // Internal camera trigger points are entry point, plus all points before exit point + missionCommandCount += transectSegment.count() - (_hasTurnaround() ? 2 : 0) - 1; + } else if (_triggerCamera() && !_imagesEverywhere()) { + // Camera on/off at entry/exit of each transect + missionCommandCount += 2; + } + } + if (transectSegments.count() && _triggerCamera() && _imagesEverywhere()) { + // Camera on/off for entire survey + missionCommandCount += 2; + } + + return missionCommandCount; +} void SurveyMissionItem::_generateGrid(void) { if (_ignoreRecalc) { @@ -720,24 +742,13 @@ void SurveyMissionItem::_generateGrid(void) if (_hoverAndCaptureEnabled()) { _additionalFlightDelaySeconds = cameraShots * _hoverAndCaptureDelaySeconds; } - emit additionalTimeDelayChanged(_additionalFlightDelaySeconds); + emit additionalTimeDelayChanged(); emit gridPointsChanged(); // Determine command count for lastSequenceNumber - - _missionCommandCount= 0; - for (int i=0; i<_transectSegments.count(); i++) { - const QList& transectSegment = _transectSegments[i]; - - _missionCommandCount += transectSegment.count(); // This accounts for all waypoints - if (_hoverAndCaptureEnabled()) { - // Internal camera trigger points are entry point, plus all points before exit point - _missionCommandCount += transectSegment.count() - (_hasTurnaround() ? 2 : 0) - 1; - } else if (_triggerCamera()) { - _missionCommandCount += 2; // Camera on/off at entry/exit - } - } + _missionCommandCount = _calcMissionCommandCount(_transectSegments); + _missionCommandCount += _calcMissionCommandCount(_reflyTransectSegments); emit lastSequenceNumberChanged(lastSequenceNumber()); // Set exit coordinate @@ -929,7 +940,7 @@ int SurveyMissionItem::_gridGenerator(const QList& polygonPoints, QLis // Transects are generated to be as long as the largest width/height of the bounding rect plus some fudge factor. // This way they will always be guaranteed to intersect with a polygon edge no matter what angle they are rotated to. // They are initially generated with the transects flowing from west to east and then points within the transect north to south. - double maxWidth = qMax(boundingRect.width(), boundingRect.height()) + 100.0; + double maxWidth = qMax(boundingRect.width(), boundingRect.height()) + 2000.0; double halfWidth = maxWidth / 2.0; double transectX = boundingCenter.x() - halfWidth; double transectXMax = transectX + maxWidth; @@ -1117,7 +1128,7 @@ bool SurveyMissionItem::_appendMissionItemsWorker(QList& items, QO { bool firstWaypointTrigger = false; - qCDebug(SurveyMissionItemLog) << "hasTurnaround:triggerCamera:hoverAndCapture:imagesEverywhere:hasRefly:buildRefly" << _hasTurnaround() << _triggerCamera() << _hoverAndCaptureEnabled() << _imagesEverywhere() << hasRefly << buildRefly; + qCDebug(SurveyMissionItemLog) << QStringLiteral("hasTurnaround(%1) triggerCamera(%2) hoverAndCapture(%3) imagesEverywhere(%4) hasRefly(%5) buildRefly(%6) ").arg(_hasTurnaround()).arg(_triggerCamera()).arg(_hoverAndCaptureEnabled()).arg(_imagesEverywhere()).arg(hasRefly).arg(buildRefly); QList>& transectSegments = buildRefly ? _reflyTransectSegments : _transectSegments; diff --git a/src/MissionManager/SurveyMissionItem.h b/src/MissionManager/SurveyMissionItem.h index c3a02e012780bbe807fdb76c5e3ef27686de916c..307042c77dd03af635e917cfca7a3a8bce214f61 100644 --- a/src/MissionManager/SurveyMissionItem.h +++ b/src/MissionManager/SurveyMissionItem.h @@ -117,6 +117,7 @@ public: int sequenceNumber (void) const final { return _sequenceNumber; } double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } void appendMissionItems (QList& items, QObject* missionItemParent) final; void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; void applyNewAltitude (double newAltitude) final; @@ -222,6 +223,7 @@ private: void _adjustTransectsToEntryPointLocation(QList>& transects); bool _gridAngleIsNorthSouthTransects(); double _clampGridAngle90(double gridAngle); + int _calcMissionCommandCount(QList>& transectSegments); int _sequenceNumber; bool _dirty; @@ -294,7 +296,7 @@ private: static const char* _jsonFixedValueIsAltitudeKey; static const char* _jsonRefly90DegreesKey; - static const int _hoverAndCaptureDelaySeconds = 1; + static const int _hoverAndCaptureDelaySeconds = 4; }; #endif diff --git a/src/MissionManager/SurveyMissionItemTest.cc b/src/MissionManager/SurveyMissionItemTest.cc index 7b207797b5cac97f1de9489092d9c8267ddccf17..0f021051f250db374d7401a0378ea6e8aa72afb9 100644 --- a/src/MissionManager/SurveyMissionItemTest.cc +++ b/src/MissionManager/SurveyMissionItemTest.cc @@ -200,14 +200,17 @@ double SurveyMissionItemTest::_clampGridAngle180(double gridAngle) return gridAngle; } -void SurveyMissionItemTest::_testGridAngle(void) +void SurveyMissionItemTest::_setPolygon(void) { - QGCMapPolygon* mapPolygon = _surveyItem->mapPolygon(); - for (int i=0; i<_polyPoints.count(); i++) { QGeoCoordinate& vertex = _polyPoints[i]; - mapPolygon->appendVertex(vertex); + _mapPolygon->appendVertex(vertex); } +} + +void SurveyMissionItemTest::_testGridAngle(void) +{ + _setPolygon(); for (double gridAngle=-360.0; gridAngle<=360.0; gridAngle++) { _surveyItem->gridAngle()->setRawValue(gridAngle); @@ -223,12 +226,7 @@ void SurveyMissionItemTest::_testGridAngle(void) void SurveyMissionItemTest::_testEntryLocation(void) { - QGCMapPolygon* mapPolygon = _surveyItem->mapPolygon(); - - for (int i=0; i<_polyPoints.count(); i++) { - QGeoCoordinate& vertex = _polyPoints[i]; - mapPolygon->appendVertex(vertex); - } + _setPolygon(); for (double gridAngle=-360.0; gridAngle<=360.0; gridAngle++) { _surveyItem->gridAngle()->setRawValue(gridAngle); @@ -251,3 +249,39 @@ void SurveyMissionItemTest::_testEntryLocation(void) rgSeenEntryCoords.clear(); } } + + +void SurveyMissionItemTest::_testItemCount(void) +{ + QList items; + + _setPolygon(); + + _surveyItem->hoverAndCapture()->setRawValue(false); + _surveyItem->cameraTriggerInTurnaround()->setRawValue(false); + _surveyItem->setRefly90Degrees(false); + _surveyItem->appendMissionItems(items, this); + QCOMPARE(items.count(), _surveyItem->lastSequenceNumber()); + items.clear(); + + _surveyItem->hoverAndCapture()->setRawValue(false); + _surveyItem->cameraTriggerInTurnaround()->setRawValue(true); + _surveyItem->setRefly90Degrees(false); + _surveyItem->appendMissionItems(items, this); + QCOMPARE(items.count(), _surveyItem->lastSequenceNumber()); + items.clear(); + + _surveyItem->hoverAndCapture()->setRawValue(true); + _surveyItem->cameraTriggerInTurnaround()->setRawValue(false); + _surveyItem->setRefly90Degrees(false); + _surveyItem->appendMissionItems(items, this); + QCOMPARE(items.count(), _surveyItem->lastSequenceNumber()); + items.clear(); + + _surveyItem->hoverAndCapture()->setRawValue(true); + _surveyItem->cameraTriggerInTurnaround()->setRawValue(false); + _surveyItem->setRefly90Degrees(true); + _surveyItem->appendMissionItems(items, this); + QCOMPARE(items.count(), _surveyItem->lastSequenceNumber()); + items.clear(); +} diff --git a/src/MissionManager/SurveyMissionItemTest.h b/src/MissionManager/SurveyMissionItemTest.h index db5969207fbe20a9e7aa557d9334e597706e5bd4..0fe45131f79d17f2446e8218a40d1d6ad06d9685 100644 --- a/src/MissionManager/SurveyMissionItemTest.h +++ b/src/MissionManager/SurveyMissionItemTest.h @@ -36,9 +36,11 @@ private slots: void _testCameraTrigger(void); void _testGridAngle(void); void _testEntryLocation(void); + void _testItemCount(void); private: double _clampGridAngle180(double gridAngle); + void _setPolygon(void); enum { gridPointsChangedIndex = 0, diff --git a/src/MissionManager/TransectStyle.SettingsGroup.json b/src/MissionManager/TransectStyle.SettingsGroup.json new file mode 100644 index 0000000000000000000000000000000000000000..d949f6ba24ec433e4aa073df3692bd887c904882 --- /dev/null +++ b/src/MissionManager/TransectStyle.SettingsGroup.json @@ -0,0 +1,38 @@ +[ +{ + "name": "TurnAroundDistance", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turn around.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 30 +}, +{ + "name": "TurnAroundDistanceMultiRotor", + "shortDescription": "Amount of additional distance to add outside the survey area for vehicle turn around.", + "type": "double", + "decimalPlaces": 2, + "min": 0, + "units": "m", + "defaultValue": 10 +}, +{ + "name": "CameraTriggerInTurnAround", + "shortDescription": "Camera continues taking images in turn arounds.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "HoverAndCapture", + "shortDescription": "Stop and Hover at each image point before taking image", + "type": "bool", + "defaultValue": false +}, +{ + "name": "Refly90Degrees", + "shortDescription": "Refly the pattern at a 90 degree angle", + "type": "bool", + "defaultValue": false +} +] diff --git a/src/MissionManager/TransectStyleComplexItem.cc b/src/MissionManager/TransectStyleComplexItem.cc new file mode 100644 index 0000000000000000000000000000000000000000..27c1f54018ff3df37873a45df5a86ffbe5fd83b7 --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItem.cc @@ -0,0 +1,232 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "TransectStyleComplexItem.h" +#include "JsonHelper.h" +#include "MissionController.h" +#include "QGCGeo.h" +#include "QGroundControlQmlGlobal.h" +#include "QGCQGeoCoordinate.h" +#include "SettingsManager.h" +#include "AppSettings.h" +#include "QGCQGeoCoordinate.h" + +#include + +QGC_LOGGING_CATEGORY(TransectStyleComplexItemLog, "TransectStyleComplexItemLog") + +const char* TransectStyleComplexItem::turnAroundDistanceName = "TurnAroundDistance"; +const char* TransectStyleComplexItem::turnAroundDistanceMultiRotorName = "TurnAroundDistanceMultiRotor"; +const char* TransectStyleComplexItem::cameraTriggerInTurnAroundName = "CameraTriggerInTurnAround"; +const char* TransectStyleComplexItem::hoverAndCaptureName = "HoverAndCapture"; +const char* TransectStyleComplexItem::refly90DegreesName = "Refly90Degrees"; + +const char* TransectStyleComplexItem::_jsonCameraCalcKey = "CameraCalc"; + +TransectStyleComplexItem::TransectStyleComplexItem(Vehicle* vehicle, QString settingsGroup, QObject* parent) + : ComplexMissionItem (vehicle, parent) + , _settingsGroup (settingsGroup) + , _sequenceNumber (0) + , _dirty (false) + , _ignoreRecalc (false) + , _complexDistance (0) + , _cameraShots (0) + , _cameraMinTriggerInterval (0) + , _cameraCalc (vehicle) + , _metaDataMap (FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/TransectStyle.SettingsGroup.json"), this)) + , _turnAroundDistanceFact (_settingsGroup, _metaDataMap[_vehicle->multiRotor() ? turnAroundDistanceMultiRotorName : turnAroundDistanceName]) + , _cameraTriggerInTurnAroundFact(_settingsGroup, _metaDataMap[cameraTriggerInTurnAroundName]) + , _hoverAndCaptureFact (_settingsGroup, _metaDataMap[hoverAndCaptureName]) + , _refly90DegreesFact (_settingsGroup, _metaDataMap[refly90DegreesName]) +{ + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(&_hoverAndCaptureFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(&_refly90DegreesFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(&_cameraTriggerInTurnAroundFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + connect(_cameraCalc.adjustedFootprintFrontal(), &Fact::valueChanged, this, &TransectStyleComplexItem::_rebuildTransects); + + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(&_hoverAndCaptureFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(&_refly90DegreesFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(&_cameraTriggerInTurnAroundFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(_cameraCalc.adjustedFootprintSide(), &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + connect(_cameraCalc.adjustedFootprintFrontal(), &Fact::valueChanged, this, &TransectStyleComplexItem::_signalLastSequenceNumberChanged); + + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + connect(&_hoverAndCaptureFact, &Fact::valueChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + connect(&_refly90DegreesFact, &Fact::valueChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); + connect(&_hoverAndCaptureFact, &Fact::valueChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); + connect(&_refly90DegreesFact, &Fact::valueChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); + + connect(&_turnAroundDistanceFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_setDirty); + connect(&_cameraTriggerInTurnAroundFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_setDirty); + connect(&_hoverAndCaptureFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_setDirty); + connect(&_refly90DegreesFact, &Fact::valueChanged, this, &TransectStyleComplexItem::_setDirty); + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::_setDirty); + + connect(&_surveyAreaPolygon, &QGCMapPolygon::dirtyChanged, this, &TransectStyleComplexItem::_setIfDirty); + connect(&_cameraCalc, &CameraCalc::dirtyChanged, this, &TransectStyleComplexItem::_setIfDirty); + + connect(&_surveyAreaPolygon, &QGCMapPolygon::pathChanged, this, &TransectStyleComplexItem::coveredAreaChanged); + + connect(this, &TransectStyleComplexItem::transectPointsChanged, this, &TransectStyleComplexItem::complexDistanceChanged); + connect(this, &TransectStyleComplexItem::transectPointsChanged, this, &TransectStyleComplexItem::greatestDistanceToChanged); +} + +void TransectStyleComplexItem::_setCameraShots(int cameraShots) +{ + if (_cameraShots != cameraShots) { + _cameraShots = cameraShots; + emit cameraShotsChanged(); + } +} + +void TransectStyleComplexItem::setDirty(bool dirty) +{ + if (!dirty) { + _surveyAreaPolygon.setDirty(false); + _cameraCalc.setDirty(false); + } + if (_dirty != dirty) { + _dirty = dirty; + emit dirtyChanged(_dirty); + } +} + +void TransectStyleComplexItem::_save(QJsonObject& complexObject) +{ + complexObject[turnAroundDistanceName] = _turnAroundDistanceFact.rawValue().toDouble(); + complexObject[cameraTriggerInTurnAroundName] = _cameraTriggerInTurnAroundFact.rawValue().toBool(); + complexObject[hoverAndCaptureName] = _hoverAndCaptureFact.rawValue().toBool(); + complexObject[refly90DegreesName] = _refly90DegreesFact.rawValue().toBool(); + + QJsonObject cameraCalcObject; + _cameraCalc.save(cameraCalcObject); + complexObject[_jsonCameraCalcKey] = cameraCalcObject; +} + +void TransectStyleComplexItem::setSequenceNumber(int sequenceNumber) +{ + if (_sequenceNumber != sequenceNumber) { + _sequenceNumber = sequenceNumber; + emit sequenceNumberChanged(sequenceNumber); + emit lastSequenceNumberChanged(lastSequenceNumber()); + } +} + +bool TransectStyleComplexItem::_load(const QJsonObject& complexObject, QString& errorString) +{ + QList keyInfoList = { + { turnAroundDistanceName, QJsonValue::Double, true }, + { cameraTriggerInTurnAroundName, QJsonValue::Bool, true }, + { hoverAndCaptureName, QJsonValue::Bool, true }, + { refly90DegreesName, QJsonValue::Bool, true }, + { _jsonCameraCalcKey, QJsonValue::Object, true }, + }; + if (!JsonHelper::validateKeys(complexObject, keyInfoList, errorString)) { + return false; + } + + if (!_cameraCalc.load(complexObject[_jsonCameraCalcKey].toObject(), errorString)) { + return false; + } + + _turnAroundDistanceFact.setRawValue (complexObject[turnAroundDistanceName].toDouble()); + _cameraTriggerInTurnAroundFact.setRawValue (complexObject[cameraTriggerInTurnAroundName].toBool()); + _hoverAndCaptureFact.setRawValue (complexObject[hoverAndCaptureName].toBool()); + _hoverAndCaptureFact.setRawValue (complexObject[refly90DegreesName].toBool()); + + return true; +} + +double TransectStyleComplexItem::greatestDistanceTo(const QGeoCoordinate &other) const +{ + double greatestDistance = 0.0; + for (int i=0; i<_transectPoints.count(); i++) { + QGeoCoordinate vertex = _transectPoints[i].value(); + double distance = vertex.distanceTo(other); + if (distance > greatestDistance) { + greatestDistance = distance; + } + } + + return greatestDistance; +} + +void TransectStyleComplexItem::setMissionFlightStatus(MissionController::MissionFlightStatus_t& missionFlightStatus) +{ + ComplexMissionItem::setMissionFlightStatus(missionFlightStatus); + if (!qFuzzyCompare(_cruiseSpeed, missionFlightStatus.vehicleSpeed)) { + _cruiseSpeed = missionFlightStatus.vehicleSpeed; + emit timeBetweenShotsChanged(); + } +} + +void TransectStyleComplexItem::_setDirty(void) +{ + setDirty(true); +} + +void TransectStyleComplexItem::_setIfDirty(bool dirty) +{ + if (dirty) { + setDirty(true); + } +} + +void TransectStyleComplexItem::applyNewAltitude(double newAltitude) +{ + Q_UNUSED(newAltitude); + // FIXME: NYI + //_altitudeFact.setRawValue(newAltitude); +} + +double TransectStyleComplexItem::timeBetweenShots(void) +{ + return _cruiseSpeed == 0 ? 0 : _cameraCalc.adjustedFootprintSide()->rawValue().toDouble() / _cruiseSpeed; +} + +void TransectStyleComplexItem::_updateCoordinateAltitudes(void) +{ + emit coordinateChanged(coordinate()); + emit exitCoordinateChanged(exitCoordinate()); +} + +void TransectStyleComplexItem::_signalLastSequenceNumberChanged(void) +{ + emit lastSequenceNumberChanged(lastSequenceNumber()); +} + +double TransectStyleComplexItem::coveredArea(void) const +{ + return _surveyAreaPolygon.area(); +} + +bool TransectStyleComplexItem::_hasTurnaround(void) const +{ + return _turnaroundDistance() > 0; +} + +double TransectStyleComplexItem::_turnaroundDistance(void) const +{ + return _turnAroundDistanceFact.rawValue().toDouble(); +} + +bool TransectStyleComplexItem::hoverAndCaptureAllowed(void) const +{ + return _vehicle->multiRotor() || _vehicle->vtol(); +} + diff --git a/src/MissionManager/TransectStyleComplexItem.h b/src/MissionManager/TransectStyleComplexItem.h new file mode 100644 index 0000000000000000000000000000000000000000..d624557eb4509d5736600147aa364a7b1253bb29 --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItem.h @@ -0,0 +1,152 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "ComplexMissionItem.h" +#include "MissionItem.h" +#include "SettingsFact.h" +#include "QGCLoggingCategory.h" +#include "QGCMapPolyline.h" +#include "QGCMapPolygon.h" +#include "CameraCalc.h" + +Q_DECLARE_LOGGING_CATEGORY(TransectStyleComplexItemLog) + +class TransectStyleComplexItem : public ComplexMissionItem +{ + Q_OBJECT + +public: + TransectStyleComplexItem(Vehicle* vehicle, QString settignsGroup, QObject* parent = NULL); + + Q_PROPERTY(QGCMapPolygon* surveyAreaPolygon READ surveyAreaPolygon CONSTANT) + Q_PROPERTY(CameraCalc* cameraCalc READ cameraCalc CONSTANT) + Q_PROPERTY(Fact* turnAroundDistance READ turnAroundDistance CONSTANT) + Q_PROPERTY(Fact* cameraTriggerInTurnAround READ cameraTriggerInTurnAround CONSTANT) + Q_PROPERTY(Fact* hoverAndCapture READ hoverAndCapture CONSTANT) + Q_PROPERTY(Fact* refly90Degrees READ refly90Degrees CONSTANT) + + Q_PROPERTY(int cameraShots READ cameraShots NOTIFY cameraShotsChanged) + Q_PROPERTY(double timeBetweenShots READ timeBetweenShots NOTIFY timeBetweenShotsChanged) + Q_PROPERTY(double coveredArea READ coveredArea NOTIFY coveredAreaChanged) + Q_PROPERTY(double cameraMinTriggerInterval READ cameraMinTriggerInterval NOTIFY cameraMinTriggerIntervalChanged) + Q_PROPERTY(bool hoverAndCaptureAllowed READ hoverAndCaptureAllowed CONSTANT) + Q_PROPERTY(QVariantList transectPoints READ transectPoints NOTIFY transectPointsChanged) + + QGCMapPolygon* surveyAreaPolygon (void) { return &_surveyAreaPolygon; } + CameraCalc* cameraCalc (void) { return &_cameraCalc; } + QVariantList transectPoints (void) { return _transectPoints; } + + Fact* turnAroundDistance (void) { return &_turnAroundDistanceFact; } + Fact* cameraTriggerInTurnAround (void) { return &_cameraTriggerInTurnAroundFact; } + Fact* hoverAndCapture (void) { return &_hoverAndCaptureFact; } + Fact* refly90Degrees (void) { return &_refly90DegreesFact; } + + int cameraShots (void) const { return _cameraShots; } + double timeBetweenShots (void); + double coveredArea (void) const; + double cameraMinTriggerInterval(void) const { return _cameraMinTriggerInterval; } + bool hoverAndCaptureAllowed (void) const; + + // Overrides from ComplexMissionItem + + int lastSequenceNumber (void) const override = 0; + QString mapVisualQML (void) const override = 0; + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) override = 0; + + double complexDistance (void) const final { return _complexDistance; } + double greatestDistanceTo (const QGeoCoordinate &other) const final; + + // Overrides from VisualMissionItem + + void save (QJsonArray& missionItems) override = 0; + bool specifiesCoordinate (void) const override = 0; + void appendMissionItems (QList& items, QObject* missionItemParent) override = 0; + void applyNewAltitude (double newAltitude) override = 0; + + bool dirty (void) const final { return _dirty; } + bool isSimpleItem (void) const final { return false; } + bool isStandaloneCoordinate (void) const final { return false; } + bool specifiesAltitudeOnly (void) const final { return false; } + QString commandDescription (void) const final { return tr("Corridor Scan"); } + QString commandName (void) const final { return tr("Corridor Scan"); } + QString abbreviation (void) const final { return "S"; } + QGeoCoordinate coordinate (void) const final { return _coordinate; } + QGeoCoordinate exitCoordinate (void) const final { return _exitCoordinate; } + int sequenceNumber (void) const final { return _sequenceNumber; } + double specifiedFlightSpeed (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalYaw (void) final { return std::numeric_limits::quiet_NaN(); } + double specifiedGimbalPitch (void) final { return std::numeric_limits::quiet_NaN(); } + void setMissionFlightStatus (MissionController::MissionFlightStatus_t& missionFlightStatus) final; + + bool coordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } + bool exitCoordinateHasRelativeAltitude (void) const final { return true /*_altitudeRelative*/; } + bool exitCoordinateSameAsEntry (void) const final { return false; } + + void setDirty (bool dirty) final; + void setCoordinate (const QGeoCoordinate& coordinate) final { Q_UNUSED(coordinate); } + void setSequenceNumber (int sequenceNumber) final; + + static const char* turnAroundDistanceName; + static const char* turnAroundDistanceMultiRotorName; + static const char* cameraTriggerInTurnAroundName; + static const char* hoverAndCaptureName; + static const char* refly90DegreesName; + +signals: + void cameraShotsChanged (void); + void timeBetweenShotsChanged (void); + void cameraMinTriggerIntervalChanged(double cameraMinTriggerInterval); + void transectPointsChanged (void); + void coveredAreaChanged (void); + +protected slots: + virtual void _rebuildTransects (void) = 0; + + void _setDirty (void); + void _setIfDirty (bool dirty); + void _updateCoordinateAltitudes (void); + void _signalLastSequenceNumberChanged (void); + +protected: + void _save (QJsonObject& saveObject); + bool _load (const QJsonObject& complexObject, QString& errorString); + void _setExitCoordinate (const QGeoCoordinate& coordinate); + void _setCameraShots (int cameraShots); + double _triggerDistance (void) const; + int _transectCount (void) const; + bool _hasTurnaround (void) const; + double _turnaroundDistance (void) const; + + QString _settingsGroup; + int _sequenceNumber; + bool _dirty; + QGeoCoordinate _coordinate; + QGeoCoordinate _exitCoordinate; + QVariantList _transectPoints; + QGCMapPolygon _surveyAreaPolygon; + + bool _ignoreRecalc; + double _complexDistance; + int _cameraShots; + double _timeBetweenShots; + double _cameraMinTriggerInterval; + double _cruiseSpeed; + CameraCalc _cameraCalc; + + QMap _metaDataMap; + + SettingsFact _turnAroundDistanceFact; + SettingsFact _cameraTriggerInTurnAroundFact; + SettingsFact _hoverAndCaptureFact; + SettingsFact _refly90DegreesFact; + + static const char* _jsonCameraCalcKey; +}; diff --git a/src/MissionManager/TransectStyleComplexItemTest.cc b/src/MissionManager/TransectStyleComplexItemTest.cc new file mode 100644 index 0000000000000000000000000000000000000000..a9f2372af32e32225d8bfdbf1612ce063f09f790 --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItemTest.cc @@ -0,0 +1,180 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "TransectStyleComplexItemTest.h" +#include "QGCApplication.h" + +TransectStyleComplexItemTest::TransectStyleComplexItemTest(void) + : _offlineVehicle(NULL) +{ + _polygonVertices << QGeoCoordinate(47.633550640000003, -122.08982199) + << QGeoCoordinate(47.634129020000003, -122.08887249) + << QGeoCoordinate(47.633619320000001, -122.08811074) + << QGeoCoordinate(47.633189139999999, -122.08900124); +} + +void TransectStyleComplexItemTest::init(void) +{ + UnitTest::init(); + + _offlineVehicle = new Vehicle(MAV_AUTOPILOT_PX4, MAV_TYPE_QUADROTOR, qgcApp()->toolbox()->firmwarePluginManager(), this); + _transectStyleItem = new TransectStyleItem(_offlineVehicle, this); + _setSurveyAreaPolygon(); + _transectStyleItem->setDirty(false); + + _rgSignals[cameraShotsChangedIndex] = SIGNAL(cameraShotsChanged()); + _rgSignals[timeBetweenShotsChangedIndex] = SIGNAL(timeBetweenShotsChanged()); + _rgSignals[cameraMinTriggerIntervalChangedIndex] = SIGNAL(cameraMinTriggerIntervalChanged(double)); + _rgSignals[transectPointsChangedIndex] = SIGNAL(transectPointsChanged()); + _rgSignals[coveredAreaChangedIndex] = SIGNAL(coveredAreaChanged()); + _rgSignals[dirtyChangedIndex] = SIGNAL(dirtyChanged(bool)); + _rgSignals[complexDistanceChangedIndex] = SIGNAL(complexDistanceChanged()); + _rgSignals[greatestDistanceToChangedIndex] = SIGNAL(greatestDistanceToChanged()); + _rgSignals[additionalTimeDelayChangedIndex] = SIGNAL(additionalTimeDelayChanged()); + _rgSignals[lastSequenceNumberChangedIndex] = SIGNAL(lastSequenceNumberChanged(int)); + + _multiSpy = new MultiSignalSpy(); + QCOMPARE(_multiSpy->init(_transectStyleItem, _rgSignals, _cSignals), true); +} + +void TransectStyleComplexItemTest::cleanup(void) +{ + delete _transectStyleItem; + delete _offlineVehicle; + delete _multiSpy; +} + +void TransectStyleComplexItemTest::_testDirty(void) +{ + QVERIFY(!_transectStyleItem->dirty()); + _transectStyleItem->setDirty(false); + QVERIFY(!_transectStyleItem->dirty()); + QVERIFY(_multiSpy->checkNoSignals()); + + _transectStyleItem->setDirty(true); + QVERIFY(_transectStyleItem->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + QVERIFY(_multiSpy->pullBoolFromSignalIndex(dirtyChangedIndex)); + _multiSpy->clearAllSignals(); + + _transectStyleItem->setDirty(false); + QVERIFY(!_transectStyleItem->dirty()); + QVERIFY(_multiSpy->checkOnlySignalByMask(dirtyChangedMask)); + _multiSpy->clearAllSignals(); + + // These facts should set dirty when changed + QList rgFacts; + rgFacts << _transectStyleItem->turnAroundDistance() + << _transectStyleItem->cameraTriggerInTurnAround() + << _transectStyleItem->hoverAndCapture() + << _transectStyleItem->refly90Degrees(); + foreach(Fact* fact, rgFacts) { + qDebug() << fact->name(); + QVERIFY(!_transectStyleItem->dirty()); + changeFactValue(fact); + QVERIFY(_multiSpy->checkSignalByMask(dirtyChangedMask)); + _transectStyleItem->setDirty(false); + _multiSpy->clearAllSignals(); + } + rgFacts.clear(); + + _adjustSurveAreaPolygon(); + QVERIFY(_transectStyleItem->dirty()); + _transectStyleItem->setDirty(false); + QVERIFY(!_transectStyleItem->surveyAreaPolygon()->dirty()); + _multiSpy->clearAllSignals(); + + changeFactValue(_transectStyleItem->cameraCalc()->distanceToSurface()); + QVERIFY(_transectStyleItem->dirty()); + _transectStyleItem->setDirty(false); + QVERIFY(!_transectStyleItem->cameraCalc()->dirty()); + _multiSpy->clearAllSignals(); +} + +void TransectStyleComplexItemTest::_setSurveyAreaPolygon(void) +{ + foreach (const QGeoCoordinate vertex, _polygonVertices) { + _transectStyleItem->surveyAreaPolygon()->appendVertex(vertex); + } +} + +void TransectStyleComplexItemTest::_testRebuildTransects(void) +{ + // Changing the survey polygon should trigger: + // _rebuildTransects call + // coveredAreaChanged signal + // lastSequenceNumberChanged signal + _adjustSurveAreaPolygon(); + QVERIFY(_transectStyleItem->rebuildTransectsCalled); + QVERIFY(_multiSpy->checkSignalsByMask(coveredAreaChangedMask | lastSequenceNumberChangedMask)); + _transectStyleItem->rebuildTransectsCalled = false; + _transectStyleItem->setDirty(false); + _multiSpy->clearAllSignals(); + + // Changes to these facts should trigger: + // _rebuildTransects call + // lastSequenceNumberChanged signal + QList rgFacts; + rgFacts << _transectStyleItem->turnAroundDistance() + << _transectStyleItem->cameraTriggerInTurnAround() + << _transectStyleItem->hoverAndCapture() + << _transectStyleItem->refly90Degrees() + << _transectStyleItem->cameraCalc()->adjustedFootprintSide() + << _transectStyleItem->cameraCalc()->adjustedFootprintFrontal(); + foreach(Fact* fact, rgFacts) { + qDebug() << fact->name(); + changeFactValue(fact); + QVERIFY(_transectStyleItem->rebuildTransectsCalled); + QVERIFY(_multiSpy->checkSignalsByMask(lastSequenceNumberChangedMask)); + _transectStyleItem->setDirty(false); + _multiSpy->clearAllSignals(); + _transectStyleItem->rebuildTransectsCalled = false; + } + rgFacts.clear(); +} + +void TransectStyleComplexItemTest::_testDistanceSignalling(void) +{ + _adjustSurveAreaPolygon(); + QVERIFY(_multiSpy->checkSignalsByMask(complexDistanceChangedMask | greatestDistanceToChangedMask)); + _transectStyleItem->setDirty(false); + _multiSpy->clearAllSignals(); + + QList rgFacts; + rgFacts << _transectStyleItem->turnAroundDistance() + << _transectStyleItem->hoverAndCapture() + << _transectStyleItem->refly90Degrees(); + foreach(Fact* fact, rgFacts) { + qDebug() << fact->name(); + changeFactValue(fact); + QVERIFY(_multiSpy->checkSignalsByMask(complexDistanceChangedMask | greatestDistanceToChangedMask)); + _transectStyleItem->setDirty(false); + _multiSpy->clearAllSignals(); + } + rgFacts.clear(); +} + +void TransectStyleComplexItemTest::_adjustSurveAreaPolygon(void) +{ + QGeoCoordinate vertex = _transectStyleItem->surveyAreaPolygon()->vertexCoordinate(0); + vertex.setLatitude(vertex.latitude() + 1); + _transectStyleItem->surveyAreaPolygon()->adjustVertex(0, vertex); +} + +TransectStyleItem::TransectStyleItem(Vehicle* vehicle, QObject* parent) + : TransectStyleComplexItem (vehicle, QStringLiteral("UnitTestTransect"), parent) + , rebuildTransectsCalled (false) +{ + +} + +void TransectStyleItem::_rebuildTransects(void) +{ + rebuildTransectsCalled = true; +} diff --git a/src/MissionManager/TransectStyleComplexItemTest.h b/src/MissionManager/TransectStyleComplexItemTest.h new file mode 100644 index 0000000000000000000000000000000000000000..d3aeea3202af69898f5d74f5098f99b84182756f --- /dev/null +++ b/src/MissionManager/TransectStyleComplexItemTest.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "UnitTest.h" +#include "MultiSignalSpy.h" +#include "CorridorScanComplexItem.h" + +#include + +class TransectStyleItem; + +class TransectStyleComplexItemTest : public UnitTest +{ + Q_OBJECT + +public: + TransectStyleComplexItemTest(void); + +protected: + void init(void) final; + void cleanup(void) final; + +private slots: + void _testDirty (void); + void _testRebuildTransects (void); + void _testDistanceSignalling (void); + +private: + void _setSurveyAreaPolygon (void); + void _adjustSurveAreaPolygon(void); + + enum { + // These signals are from TransectStyleComplexItem + cameraShotsChangedIndex = 0, + timeBetweenShotsChangedIndex, + cameraMinTriggerIntervalChangedIndex, + transectPointsChangedIndex, + coveredAreaChangedIndex, + // These signals are from ComplexItem + dirtyChangedIndex, + complexDistanceChangedIndex, + greatestDistanceToChangedIndex, + additionalTimeDelayChangedIndex, + // These signals are from VisualMissionItem + lastSequenceNumberChangedIndex, + maxSignalIndex + }; + + enum { + // These signals are from TransectStyleComplexItem + cameraShotsChangedMask = 1 << cameraShotsChangedIndex, + timeBetweenShotsChangedMask = 1 << timeBetweenShotsChangedIndex, + cameraMinTriggerIntervalChangedMask = 1 << cameraMinTriggerIntervalChangedIndex, + transectPointsChangedMask = 1 << transectPointsChangedIndex, + coveredAreaChangedMask = 1 << coveredAreaChangedIndex, + // These signals are from ComplexItem + dirtyChangedMask = 1 << dirtyChangedIndex, + complexDistanceChangedMask = 1 << complexDistanceChangedIndex, + greatestDistanceToChangedMask = 1 << greatestDistanceToChangedIndex, + additionalTimeDelayChangedMask = 1 << additionalTimeDelayChangedIndex, + // These signals are from VisualMissionItem + lastSequenceNumberChangedMask = 1 << lastSequenceNumberChangedIndex, + }; + + static const size_t _cSignals = maxSignalIndex; + const char* _rgSignals[_cSignals]; + + Vehicle* _offlineVehicle; + MultiSignalSpy* _multiSpy; + QList _polygonVertices; + TransectStyleItem* _transectStyleItem; +}; + +class TransectStyleItem : public TransectStyleComplexItem +{ + Q_OBJECT + +public: + TransectStyleItem(Vehicle* vehicle, QObject* parent = NULL); + + // Overrides from ComplexMissionItem + int lastSequenceNumber (void) const final { return _sequenceNumber; } + QString mapVisualQML (void) const final { return QString(); } + bool load (const QJsonObject& complexObject, int sequenceNumber, QString& errorString) final { Q_UNUSED(complexObject); Q_UNUSED(sequenceNumber); Q_UNUSED(errorString); return false; } + + // Overrides from VisualMissionItem + void save (QJsonArray& missionItems) final { Q_UNUSED(missionItems); } + bool specifiesCoordinate (void) const final { return true; } + void appendMissionItems (QList& items, QObject* missionItemParent) final { Q_UNUSED(items); Q_UNUSED(missionItemParent); } + void applyNewAltitude (double newAltitude) final { Q_UNUSED(newAltitude); } + + bool rebuildTransectsCalled; + +private slots: + // Overrides from TransectStyleComplexItem + void _rebuildTransects (void) final; +}; diff --git a/src/MissionManager/UnitTest/PolygonAreaTest.kml b/src/MissionManager/UnitTest/PolygonAreaTest.kml new file mode 100755 index 0000000000000000000000000000000000000000..455643f37757e65fa10cd34aa3088ea31aafbbd6 --- /dev/null +++ b/src/MissionManager/UnitTest/PolygonAreaTest.kml @@ -0,0 +1,48 @@ + + + + AreaTestPolygon.kmz + + + normal + #s_ylw-pushpin + + + highlight + #s_ylw-pushpin_hl + + + + + + Untitled Polygon + #m_ylw-pushpin + + 1 + + + + -122.1059149362712,47.65965281788451,0 -122.1044593196253,47.66002598220988,0 -122.1047336695092,47.66034166158975,0 -122.1061470943783,47.6599810708829,0 -122.1059149362712,47.65965281788451,0 + + + + + + + diff --git a/src/MissionManager/UnitTest/BadCoordinatesNode.kml b/src/MissionManager/UnitTest/PolygonBadCoordinatesNode.kml similarity index 100% rename from src/MissionManager/UnitTest/BadCoordinatesNode.kml rename to src/MissionManager/UnitTest/PolygonBadCoordinatesNode.kml diff --git a/src/MissionManager/UnitTest/BadXml.kml b/src/MissionManager/UnitTest/PolygonBadXml.kml similarity index 100% rename from src/MissionManager/UnitTest/BadXml.kml rename to src/MissionManager/UnitTest/PolygonBadXml.kml diff --git a/src/MissionManager/UnitTest/GoodPolygon.kml b/src/MissionManager/UnitTest/PolygonGood.kml similarity index 100% rename from src/MissionManager/UnitTest/GoodPolygon.kml rename to src/MissionManager/UnitTest/PolygonGood.kml diff --git a/src/MissionManager/UnitTest/MissingPolygonNode.kml b/src/MissionManager/UnitTest/PolygonMissingNode.kml similarity index 100% rename from src/MissionManager/UnitTest/MissingPolygonNode.kml rename to src/MissionManager/UnitTest/PolygonMissingNode.kml diff --git a/src/MissionManager/VisualMissionItem.cc b/src/MissionManager/VisualMissionItem.cc index a108dde1982d2583a00f5a9fa30d2b33c2457ff9..ba309b72f6ac54b434f2ca2d5aca0f85eab7d591 100644 --- a/src/MissionManager/VisualMissionItem.cc +++ b/src/MissionManager/VisualMissionItem.cc @@ -38,11 +38,15 @@ VisualMissionItem::VisualMissionItem(Vehicle* vehicle, QObject* parent) , _lastLatTerrainQuery (0) , _lastLonTerrainQuery (0) { - _updateTerrainTimer.setInterval(500); - _updateTerrainTimer.setSingleShot(true); - connect(&_updateTerrainTimer, &QTimer::timeout, this, &VisualMissionItem::_reallyUpdateTerrainAltitude); - connect(this, &VisualMissionItem::coordinateChanged, this, &VisualMissionItem::_updateTerrainAltitude); + // Don't get terrain altitude information for submarines or boards + if (_vehicle->vehicleType() != MAV_TYPE_SUBMARINE && _vehicle->vehicleType() != MAV_TYPE_SURFACE_BOAT) { + _updateTerrainTimer.setInterval(500); + _updateTerrainTimer.setSingleShot(true); + connect(&_updateTerrainTimer, &QTimer::timeout, this, &VisualMissionItem::_reallyUpdateTerrainAltitude); + + connect(this, &VisualMissionItem::coordinateChanged, this, &VisualMissionItem::_updateTerrainAltitude); + } } VisualMissionItem::VisualMissionItem(const VisualMissionItem& other, QObject* parent) @@ -58,7 +62,11 @@ VisualMissionItem::VisualMissionItem(const VisualMissionItem& other, QObject* pa , _distance (0.0) { *this = other; - connect(this, &VisualMissionItem::coordinateChanged, this, &VisualMissionItem::_updateTerrainAltitude); + + // Don't get terrain altitude information for submarines or boats + if (_vehicle->vehicleType() != MAV_TYPE_SUBMARINE && _vehicle->vehicleType() != MAV_TYPE_SURFACE_BOAT) { + connect(this, &VisualMissionItem::coordinateChanged, this, &VisualMissionItem::_updateTerrainAltitude); + } } const VisualMissionItem& VisualMissionItem::operator=(const VisualMissionItem& other) diff --git a/src/MissionManager/VisualMissionItem.h b/src/MissionManager/VisualMissionItem.h index fbd3d34eba51bdb482a4f947557ffef4ece09f74..c473adce121a907ed16a74ead3f4e0fc92a5a8de 100644 --- a/src/MissionManager/VisualMissionItem.h +++ b/src/MissionManager/VisualMissionItem.h @@ -7,9 +7,7 @@ * ****************************************************************************/ - -#ifndef VisualMissionItem_H -#define VisualMissionItem_H +#pragma once #include #include @@ -65,7 +63,8 @@ public: Q_PROPERTY(QString mapVisualQML READ mapVisualQML CONSTANT) ///< QMl code for map visuals Q_PROPERTY(QmlObjectListModel* childItems READ childItems CONSTANT) Q_PROPERTY(double specifiedFlightSpeed READ specifiedFlightSpeed NOTIFY specifiedFlightSpeedChanged) ///< NaN if this item does not specify flight speed - Q_PROPERTY(double specifiedGimbalYaw READ specifiedGimbalYaw NOTIFY specifiedGimbalYawChanged) ///< NaN if this item goes not specify gimbal yaw + Q_PROPERTY(double specifiedGimbalYaw READ specifiedGimbalYaw NOTIFY specifiedGimbalYawChanged) ///< Gimbal yaw, NaN for not specified + Q_PROPERTY(double specifiedGimbalPitch READ specifiedGimbalPitch NOTIFY specifiedGimbalPitchChanged) ///< Gimbal pitch, NaN for not specified Q_PROPERTY(double missionGimbalYaw READ missionGimbalYaw NOTIFY missionGimbalYawChanged) ///< Current gimbal yaw state at this point in mission Q_PROPERTY(double missionVehicleYaw READ missionVehicleYaw NOTIFY missionVehicleYawChanged) ///< Expected vehicle yaw at this point in mission @@ -116,6 +115,7 @@ public: virtual int sequenceNumber (void) const = 0; virtual double specifiedFlightSpeed (void) = 0; virtual double specifiedGimbalYaw (void) = 0; + virtual double specifiedGimbalPitch (void) = 0; /// Update item to mission flight status at point where this item appears in mission. /// IMPORTANT: Overrides must call base class implementation @@ -173,6 +173,7 @@ signals: void specifiesAltitudeOnlyChanged (void); void specifiedFlightSpeedChanged (void); void specifiedGimbalYawChanged (void); + void specifiedGimbalPitchChanged (void); void lastSequenceNumberChanged (int sequenceNumber); void missionGimbalYawChanged (double missionGimbalYaw); void missionVehicleYawChanged (double missionVehicleYaw); @@ -212,5 +213,3 @@ private: double _lastLatTerrainQuery; double _lastLonTerrainQuery; }; - -#endif diff --git a/src/MissionManager/VisualMissionItemTest.cc b/src/MissionManager/VisualMissionItemTest.cc index 43a027887cad634c2e9749e9aa34952ca956f3bd..8a6e235ceaeacb6499c174b5b05895ae938aff3a 100644 --- a/src/MissionManager/VisualMissionItemTest.cc +++ b/src/MissionManager/VisualMissionItemTest.cc @@ -43,6 +43,7 @@ void VisualMissionItemTest::init(void) rgVisualItemSignals[specifiesAltitudeOnlyChangedIndex] = SIGNAL(specifiesAltitudeOnlyChanged()); rgVisualItemSignals[specifiedFlightSpeedChangedIndex] = SIGNAL(specifiedFlightSpeedChanged()); rgVisualItemSignals[specifiedGimbalYawChangedIndex] = SIGNAL(specifiedGimbalYawChanged()); + rgVisualItemSignals[specifiedGimbalPitchChangedIndex] = SIGNAL(specifiedGimbalPitchChanged()); rgVisualItemSignals[lastSequenceNumberChangedIndex] = SIGNAL(lastSequenceNumberChanged(int)); rgVisualItemSignals[missionGimbalYawChangedIndex] = SIGNAL(missionGimbalYawChanged(double)); rgVisualItemSignals[missionVehicleYawChangedIndex] = SIGNAL(missionVehicleYawChanged(double)); diff --git a/src/MissionManager/VisualMissionItemTest.h b/src/MissionManager/VisualMissionItemTest.h index 20ed2b267975c0af3e435f6e6dcba79d3e3ac681..5428bf5d4764d47bd45e1c154d25e586539eaad2 100644 --- a/src/MissionManager/VisualMissionItemTest.h +++ b/src/MissionManager/VisualMissionItemTest.h @@ -49,6 +49,7 @@ protected: specifiesAltitudeOnlyChangedIndex, specifiedFlightSpeedChangedIndex, specifiedGimbalYawChangedIndex, + specifiedGimbalPitchChangedIndex, lastSequenceNumberChangedIndex, missionGimbalYawChangedIndex, missionVehicleYawChangedIndex, @@ -77,6 +78,7 @@ protected: specifiesAltitudeOnlyChangedMask = 1 << specifiesAltitudeOnlyChangedIndex, specifiedFlightSpeedChangedMask = 1 << specifiedFlightSpeedChangedIndex, specifiedGimbalYawChangedMask = 1 << specifiedGimbalYawChangedIndex, + specifiedGimbalPitchChangedMask = 1 << specifiedGimbalPitchChangedIndex, lastSequenceNumberChangedMask = 1 << lastSequenceNumberChangedIndex, missionGimbalYawChangedMask = 1 << missionGimbalYawChangedIndex, missionVehicleYawChangedMask = 1 << missionVehicleYawChangedIndex, diff --git a/src/PlanView/CameraCalc.qml b/src/PlanView/CameraCalc.qml index 7e51f211a519cd81f09692a8cc92ae971dc35004..6f0d33e45b9ec966e36050fc3af9fb0e9b070ee1 100644 --- a/src/PlanView/CameraCalc.qml +++ b/src/PlanView/CameraCalc.qml @@ -23,7 +23,7 @@ Column { property real _margin: ScreenTools.defaultFontPixelWidth / 2 property int _cameraIndex: 1 property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 - property var _cameraList: [ qsTr("Manual (no camera specs)"), qsTr("Custom Camera") ] + property var _cameraList: [ ] property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle property var _vehicleCameraList: _vehicle ? _vehicle.staticCameraList : [] @@ -32,31 +32,18 @@ Column { readonly property int _gridTypeCamera: 2 Component.onCompleted: { + _cameraList.push(cameraCalc.manualCameraName) + _cameraList.push(cameraCalc.customCameraName) for (var i=0; i<_vehicle.staticCameraList.length; i++) { _cameraList.push(_vehicle.staticCameraList[i].name) } gridTypeCombo.model = _cameraList - if (cameraCalc.cameraSpecType === CameraCalc.CameraSpecNone) { - gridTypeCombo.currentIndex = _gridTypeManual + var knownCameraIndex = gridTypeCombo.find(cameraCalc.cameraName) + if (knownCameraIndex !== -1) { + gridTypeCombo.currentIndex = knownCameraIndex } else { - var index = -1 - for (index=0; index<_cameraList.length; index++) { - if (_cameraList[index] == cameraCalc.knownCameraName) { - break; - } - } - cameraCalc.fixedOrientation.value = false - if (index == _cameraList.length) { - gridTypeCombo.currentIndex = _gridTypeCustomCamera - } else { - gridTypeCombo.currentIndex = index - if (index != 1) { - // Specific camera is selected - var camera = _vehicleCameraList[index - _gridTypeCamera] - cameraCalc.fixedOrientation.value = camera.fixedOrientation - cameraCalc.minTriggerInterval.value = camera.minTriggerInterval - } - } + console.log("Internal error: Known camera not found", cameraCalc.cameraName) + gridTypeCombo.currentIndex = _gridTypeCustomCamera } } @@ -86,21 +73,7 @@ Column { anchors.right: parent.right model: _cameraList currentIndex: -1 - - onActivated: { - if (index == _gridTypeManual) { - cameraCalc.cameraSpecType = CameraCalc.CameraSpecNone - cameraCalc.valueSetIsDistance.value = false - } else if (index == _gridTypeCustomCamera) { - cameraCalc.cameraSpecType = CameraCalc.CameraSpecCustom - cameraCalc.knownCameraName = gridTypeCombo.textAt(index) - cameraCalc.fixedOrientation.value = false - cameraCalc.minTriggerInterval.value = 0 - } else { - cameraCalc.cameraSpecType = CameraCalc.CameraSpecKnown - cameraCalc.knownCameraName = gridTypeCombo.textAt(index) - } - } + onActivated: cameraCalc.cameraName = gridTypeCombo.textAt(index) } // QGCComboxBox // Camera based grid ui @@ -108,7 +81,7 @@ Column { anchors.left: parent.left anchors.right: parent.right spacing: _margin - visible: cameraCalc.cameraSpecType !== CameraCalc.CameraSpecNone + visible: cameraCalc.cameraName !== cameraCalc.manualCameraName Row { spacing: _margin @@ -138,7 +111,7 @@ Column { anchors.left: parent.left anchors.right: parent.right spacing: _margin - visible: cameraCalc.cameraSpecType === CameraCalc.CameraSpecCustom + visible: cameraCalc.cameraName === cameraCalc.customCameraName RowLayout { anchors.left: parent.left @@ -275,6 +248,8 @@ Column { } // Calculated values +/* + Taking these out for now since they take up so much space. May come back at a later time. GridLayout { anchors.left: parent.left anchors.right: parent.right @@ -296,6 +271,7 @@ Column { enabled: false } } // GridLayout +*/ } // Column - Camera spec based ui @@ -306,7 +282,7 @@ Column { columnSpacing: _margin rowSpacing: _margin columns: 2 - visible: cameraCalc.cameraSpecType === CameraCalc.CameraSpecNone + visible: cameraCalc.cameraName === cameraCalc.manualCameraName QGCLabel { text: distanceToSurfaceLabel } FactTextField { diff --git a/src/PlanView/CameraSection.qml b/src/PlanView/CameraSection.qml index bdbba8bc2d91fbfe147810c967fbdb0c88a3e61d..b91cd52b5e8d0413969f4bf1140185fb3b5cb3c8 100644 --- a/src/PlanView/CameraSection.qml +++ b/src/PlanView/CameraSection.qml @@ -46,7 +46,7 @@ Column { anchors.left: parent.left anchors.right: parent.right spacing: ScreenTools.defaultFontPixelWidth - visible: _camera.cameraAction.rawValue == 1 + visible: _camera.cameraAction.rawValue === 1 QGCLabel { text: qsTr("Time") @@ -62,7 +62,7 @@ Column { anchors.left: parent.left anchors.right: parent.right spacing: ScreenTools.defaultFontPixelWidth - visible: _camera.cameraAction.rawValue == 2 + visible: _camera.cameraAction.rawValue === 2 QGCLabel { text: qsTr("Distance") diff --git a/src/PlanView/CorridorScanEditor.qml b/src/PlanView/CorridorScanEditor.qml new file mode 100644 index 0000000000000000000000000000000000000000..72cd5d3d80a7bb1f5672f00b8f6373b85ead7873 --- /dev/null +++ b/src/PlanView/CorridorScanEditor.qml @@ -0,0 +1,146 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 +import QtQuick.Dialogs 1.2 +import QtQuick.Extras 1.4 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Vehicle 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.FlightMap 1.0 + +// Editor for Survery mission items +Rectangle { + id: _root + height: visible ? (editorColumn.height + (_margin * 2)) : 0 + width: availableWidth + color: qgcPal.windowShadeDark + radius: _radius + + // The following properties must be available up the hierarchy chain + //property real availableWidth ///< Width for control + //property var missionItem ///< Mission Item for editor + + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 + property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle + + function polygonCaptureStarted() { + missionItem.clearPolygon() + } + + function polygonCaptureFinished(coordinates) { + for (var i=0; i 0 && missionItem.cameraMinTriggerInterval !== 0 && missionItem.cameraMinTriggerInterval > missionItem.timeBetweenShots + } + + CameraCalc { + cameraCalc: missionItem.cameraCalc + vehicleFlightIsFrontal: true + distanceToSurfaceLabel: qsTr("Altitude") + frontalDistanceLabel: qsTr("Trigger Distance") + sideDistanceLabel: qsTr("Spacing") + } + + SectionHeader { + id: corridorHeader + text: qsTr("Corridor") + } + + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + visible: corridorHeader.checked + + QGCLabel { text: qsTr("Width") } + FactTextField { + fact: missionItem.corridorWidth + Layout.fillWidth: true + } + + QGCLabel { text: qsTr("Turnaround dist") } + FactTextField { + fact: missionItem.turnAroundDistance + Layout.fillWidth: true + } + + FactCheckBox { + text: qsTr("Take images in turnarounds") + fact: missionItem.cameraTriggerInTurnAround + enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true + Layout.columnSpan: 2 + } + + QGCCheckBox { + id: relAlt + anchors.left: parent.left + text: qsTr("Relative altitude") + checked: missionItem.cameraCalc.distanceToSurfaceRelative + enabled: missionItem.cameraCalc.isManualCamera + Layout.columnSpan: 2 + onClicked: missionItem.cameraCalc.distanceToSurfaceRelative = checked + + Connections { + target: missionItem.cameraCalc + onDistanceToSurfaceRelativeChanged: relAlt.checked = missionItem.cameraCalc.distanceToSurfaceRelative + } + } + } + + QGCButton { + text: qsTr("Rotate Entry Point") + onClicked: missionItem.rotateEntryPoint() + } + + SectionHeader { + id: statsHeader + text: qsTr("Statistics") + } + + Grid { + columns: 2 + columnSpacing: ScreenTools.defaultFontPixelWidth + visible: statsHeader.checked + + QGCLabel { text: qsTr("Photo count") } + QGCLabel { text: missionItem.cameraShots } + + QGCLabel { text: qsTr("Photo interval") } + QGCLabel { text: missionItem.timeBetweenShots.toFixed(1) + " " + qsTr("secs") } + } + } // Column +} // Rectangle diff --git a/src/PlanView/CorridorScanMapVisual.qml b/src/PlanView/CorridorScanMapVisual.qml new file mode 100644 index 0000000000000000000000000000000000000000..616120b29c42e6c6a8c8c9fe7a6b6b8469534103 --- /dev/null +++ b/src/PlanView/CorridorScanMapVisual.qml @@ -0,0 +1,125 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtLocation 5.3 +import QtPositioning 5.3 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 + +/// Corridor Scan Complex Mission Item visuals +Item { + id: _root + + property var map ///< Map control to place item in + property var qgcView ///< QGCView to use for popping dialogs + + property var _missionItem: object + property var _entryCoordinate + property var _exitCoordinate + property var _transectLines + + signal clicked(int sequenceNumber) + + function _addVisualElements() { + _entryCoordinate = entryPointComponent.createObject(map) + _exitCoordinate = exitPointComponent.createObject(map) + _transectLines = transectsComponent.createObject(map) + map.addMapItem(_entryCoordinate) + map.addMapItem(_exitCoordinate) + map.addMapItem(_transectLines) + } + + function _destroyVisualElements() { + _entryCoordinate.destroy() + _exitCoordinate.destroy() + _transectLines.destroy() + } + + Component.onCompleted: { + mapPolylineVisuals.addInitialPolyline() + _addVisualElements() + } + + Component.onDestruction: { + _destroyVisualElements() + } + + QGCMapPolygonVisuals { + qgcView: _root.qgcView + mapControl: map + mapPolygon: object.surveyAreaPolygon + interactive: false + interiorColor: "green" + interiorOpacity: 0.25 + } + + QGCMapPolylineVisuals { + id: mapPolylineVisuals + qgcView: _root.qgcView + mapControl: map + mapPolyline: object.corridorPolyline + interactive: _missionItem.isCurrentItem + lineWidth: 3 + lineColor: "#be781c" + } + + // Entry point + Component { + id: entryPointComponent + + MapQuickItem { + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.coordinate + visible: _missionItem.coordinate.isValid + + sourceItem: MissionItemIndexLabel { + index: _missionItem.sequenceNumber + label: "Entry" + checked: _missionItem.isCurrentItem + onClicked: _root.clicked(_missionItem.sequenceNumber) + } + } + } + + // Exit point + Component { + id: exitPointComponent + + MapQuickItem { + anchorPoint.x: sourceItem.anchorPointX + anchorPoint.y: sourceItem.anchorPointY + z: QGroundControl.zOrderMapItems + coordinate: _missionItem.exitCoordinate + visible: _missionItem.exitCoordinate.isValid + + sourceItem: MissionItemIndexLabel { + index: _missionItem.lastSequenceNumber + label: "Exit" + checked: _missionItem.isCurrentItem + onClicked: _root.clicked(_missionItem.sequenceNumber) + } + } + } + + // Transect lines + Component { + id: transectsComponent + + MapPolyline { + line.color: "white" + line.width: 2 + path: _missionItem.transectPoints + } + } +} diff --git a/src/PlanView/FWLandingPatternEditor.qml b/src/PlanView/FWLandingPatternEditor.qml index 6ce790a4f33a2c36b3ddc09031ba42ae6c7d41b3..b703e9a63fc1cf02a1617871734e52dc7895259b 100644 --- a/src/PlanView/FWLandingPatternEditor.qml +++ b/src/PlanView/FWLandingPatternEditor.qml @@ -52,6 +52,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right factList: [ missionItem.loiterAltitude, missionItem.loiterRadius ] + factLabels: [ qsTr("Altitude"), qsTr("Radius") ] } Item { width: 1; height: _spacer } @@ -82,14 +83,14 @@ Rectangle { anchors.right: parent.right columns: 2 - QGCLabel { text: missionItem.landingHeading.name } + QGCLabel { text: qsTr("Heading") } FactTextField { Layout.fillWidth: true fact: missionItem.landingHeading } - QGCLabel { text: missionItem.landingAltitude.name } + QGCLabel { text: qsTr("Altitude") } FactTextField { Layout.fillWidth: true @@ -98,7 +99,7 @@ Rectangle { QGCRadioButton { id: useLandingDistance - text: missionItem.landingDistance.name + text: qsTr("Landing dist") checked: !useFallRate.checked onClicked: { useFallRate.checked = false @@ -115,7 +116,7 @@ Rectangle { QGCRadioButton { id: useFallRate - text: missionItem.fallRate.name + text: qsTr("Descent rate") checked: !useLandingDistance.checked onClicked: { useLandingDistance.checked = false @@ -160,19 +161,12 @@ Rectangle { Column { id: editorColumnNeedLandingPoint anchors.margins: _margin + anchors.top: parent.top anchors.left: parent.left anchors.right: parent.right visible: !missionItem.landingCoordSet spacing: ScreenTools.defaultFontPixelHeight - QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.smallFontPointSize - text: qsTr("WIP (NOT FOR REAL FLIGHT!)") - } - QGCLabel { anchors.left: parent.left anchors.right: parent.right diff --git a/src/PlanView/GeoFenceEditor.qml b/src/PlanView/GeoFenceEditor.qml index dd592b5fe6ef150d52a78da19cbf77468424d466..8cf3cb745a4a3eded03811ca3ffd91b9fa31e400 100644 --- a/src/PlanView/GeoFenceEditor.qml +++ b/src/PlanView/GeoFenceEditor.qml @@ -154,7 +154,7 @@ QGCFlickable { QGCLabel { text: qsTr("None") - visible: polygonSection.checked && myGeoFenceController.polygons.count == 0 + visible: polygonSection.checked && myGeoFenceController.polygons.count === 0 } GridLayout { @@ -239,7 +239,7 @@ QGCFlickable { QGCLabel { text: qsTr("None") - visible: circleSection.checked && myGeoFenceController.circles.count == 0 + visible: circleSection.checked && myGeoFenceController.circles.count === 0 } GridLayout { diff --git a/src/PlanView/MissionItemEditor.qml b/src/PlanView/MissionItemEditor.qml index 1fd1167f51a9f8c551c450ac03b6ec4b4eb5cba7..18083de522dc4814967d09be7cf3d9312f2bc0ce 100644 --- a/src/PlanView/MissionItemEditor.qml +++ b/src/PlanView/MissionItemEditor.qml @@ -62,6 +62,15 @@ Rectangle { } } + Component { + id: editPositionDialog + + EditPositionDialog { + coordinate: missionItem.coordinate + onCoordinateChanged: missionItem.coordinate = coordinate + } + } + QGCLabel { id: label anchors.verticalCenter: commandPicker.verticalCenter @@ -80,7 +89,7 @@ Rectangle { height: _hamburgerSize sourceSize.height: _hamburgerSize source: "qrc:/qmlimages/Hamburger.svg" - visible: missionItem.isCurrentItem && missionItem.sequenceNumber != 0 + visible: missionItem.isCurrentItem && missionItem.sequenceNumber !== 0 color: qgcPal.windowShade } @@ -136,6 +145,12 @@ Rectangle { visible: !_waypointsOnlyMode } + MenuItem { + text: qsTr("Edit position...") + visible: missionItem.specifiesCoordinate + onTriggered: qgcView.showDialog(editPositionDialog, qsTr("Edit Position"), qgcView.showDialogDefaultWidth, StandardButton.Cancel) + } + MenuSeparator { visible: missionItem.isSimpleItem && !_waypointsOnlyMode } diff --git a/src/PlanView/MissionItemStatus.qml b/src/PlanView/MissionItemStatus.qml index dc654e965062b85eba657903b2f3b79a955d5435..e5a2dd74599ea44fb0ed3024a0381fcbcdbd6b22 100644 --- a/src/PlanView/MissionItemStatus.qml +++ b/src/PlanView/MissionItemStatus.qml @@ -20,7 +20,6 @@ import QGroundControl.FactControls 1.0 Rectangle { id: root - height: ScreenTools.defaultFontPixelHeight * 7 radius: ScreenTools.defaultFontPixelWidth * 0.5 color: qgcPal.window opacity: 0.80 @@ -32,7 +31,7 @@ Rectangle { readonly property real _margins: ScreenTools.defaultFontPixelWidth onMaxWidthChanged: { - var calcLength = (statusListView.count + 1)*statusListView.contentItem.children[0].width + var calcLength = (statusListView.count + 1) * (statusListView.count ? statusListView.contentItem.children[0].width : 1) root.width = root.maxWidth > calcLength ? calcLength : root.maxWidth } @@ -64,7 +63,7 @@ Rectangle { currentIndex: _missionController.currentPlanViewIndex onCountChanged: { - var calcLength = (statusListView.count + 1)*statusListView.contentItem.children[0].width + var calcLength = (statusListView.count + 1) * (statusListView.count ? statusListView.contentItem.children[0].width : 1) root.width = root.maxWidth > calcLength ? calcLength : root.maxWidth } diff --git a/src/PlanView/MissionSettingsEditor.qml b/src/PlanView/MissionSettingsEditor.qml index 706fb4939b2e70f939edca4d6d0f731cac97d656..9cb6368d55385bac170db72c70ce62c98e2faf6e 100644 --- a/src/PlanView/MissionSettingsEditor.qml +++ b/src/PlanView/MissionSettingsEditor.qml @@ -15,7 +15,7 @@ import QGroundControl.Controllers 1.0 Rectangle { id: valuesRect width: availableWidth - height: deferedload.status == Loader.Ready ? (visible ? deferedload.item.height : 0) : 0 + height: valuesColumn.height + (_margin * 2) color: qgcPal.windowShadeDark visible: missionItem.isCurrentItem radius: _radius @@ -36,131 +36,171 @@ Rectangle { property var _fileExtension: QGroundControl.settingsManager.appSettings.missionFileExtension property var _appSettings: QGroundControl.settingsManager.appSettings property bool _waypointsOnlyMode: QGroundControl.corePlugin.options.missionWaypointsOnly + property bool _showCameraSection: !_waypointsOnlyMode || QGroundControl.corePlugin.showAdvancedUI readonly property string _firmwareLabel: qsTr("Firmware") readonly property string _vehicleLabel: qsTr("Vehicle") + readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 QGCPalette { id: qgcPal } QGCFileDialogController { id: fileController } - Loader { - id: deferedload - active: valuesRect.visible - asynchronous: true - anchors.margins: _margin - anchors.left: valuesRect.left - anchors.right: valuesRect.right - anchors.top: valuesRect.top + Column { + id: valuesColumn + anchors.margins: _margin + anchors.left: parent.left + anchors.right: parent.right + anchors.top: parent.top + spacing: _margin - sourceComponent: Component { - Item { - id: valuesItem - height: valuesColumn.height + (_margin * 2) + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin - Column { - id: valuesColumn - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - spacing: _margin + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: ScreenTools.defaultFontPixelWidth + rowSpacing: columnSpacing + columns: 2 - Loader { - anchors.left: parent.left - anchors.right: parent.right - sourceComponent: missionSettings - } - } // Column - } // Item - } // Component - } // Loader + QGCLabel { + text: qsTr("Waypoint alt") + } + FactTextField { + fact: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude + Layout.fillWidth: true + } - Component { - id: missionSettings + QGCCheckBox { + id: flightSpeedCheckBox + text: qsTr("Flight speed") + visible: !_missionVehicle.vtol + checked: missionItem.speedSection.specifyFlightSpeed + onClicked: missionItem.speedSection.specifyFlightSpeed = checked + } + FactTextField { + Layout.fillWidth: true + fact: missionItem.speedSection.flightSpeed + visible: flightSpeedCheckBox.visible + enabled: flightSpeedCheckBox.checked + } + } // GridLayout + } + + CameraSection { + id: cameraSection + checked: missionItem.cameraSection.settingsSpecified + visible: _showCameraSection + } + + QGCLabel { + anchors.left: parent.left + anchors.right: parent.right + text: qsTr("Above camera commands will take affect immediately upon mission start.") + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font.pointSize: ScreenTools.smallFontPointSize + visible: _showCameraSection && cameraSection.checked + } + + SectionHeader { + id: missionEndHeader + text: qsTr("Mission End") + checked: true + } Column { - id: valuesColumn - anchors.left: parent ? parent.left : undefined - anchors.right: parent ? parent.right : undefined - anchors.top: parent ? parent.top : undefined + anchors.left: parent.left + anchors.right: parent.right spacing: _margin + visible: missionEndHeader.checked - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: _margin + QGCCheckBox { + text: qsTr("Return To Launch") + checked: missionItem.missionEndRTL + onClicked: missionItem.missionEndRTL = checked + } + } - GridLayout { - anchors.left: parent.left - anchors.right: parent.right - columnSpacing: ScreenTools.defaultFontPixelWidth - rowSpacing: columnSpacing - columns: 2 - QGCLabel { - text: qsTr("Waypoint alt") - } - FactTextField { - fact: QGroundControl.settingsManager.appSettings.defaultMissionItemAltitude - Layout.fillWidth: true - } + SectionHeader { + id: vehicleInfoSectionHeader + text: qsTr("Vehicle Info") + visible: _offlineEditing && !_waypointsOnlyMode + checked: false + } - QGCCheckBox { - id: flightSpeedCheckBox - text: qsTr("Flight speed") - visible: !_missionVehicle.vtol - checked: missionItem.speedSection.specifyFlightSpeed - onClicked: missionItem.speedSection.specifyFlightSpeed = checked - } - FactTextField { - Layout.fillWidth: true - fact: missionItem.speedSection.flightSpeed - visible: flightSpeedCheckBox.visible - enabled: flightSpeedCheckBox.checked - } - } // GridLayout - } + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: ScreenTools.defaultFontPixelWidth + rowSpacing: columnSpacing + columns: 2 + visible: vehicleInfoSectionHeader.visible && vehicleInfoSectionHeader.checked - CameraSection { - id: cameraSection - checked: missionItem.cameraSection.settingsSpecified + QGCLabel { + text: _firmwareLabel + Layout.fillWidth: true + visible: _showOfflineVehicleCombos + } + FactComboBox { + fact: QGroundControl.settingsManager.appSettings.offlineEditingFirmwareType + indexModel: false + Layout.preferredWidth: _fieldWidth + visible: _showOfflineVehicleCombos + enabled: _enableOfflineVehicleCombos } QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("Above camera commands will take affect immediately upon mission start.") - wrapMode: Text.WordWrap - horizontalAlignment: Text.AlignHCenter - font.pointSize: ScreenTools.smallFontPointSize - visible: cameraSection.checked + text: _vehicleLabel + Layout.fillWidth: true + visible: _showOfflineVehicleCombos } - - SectionHeader { - id: missionEndHeader - text: qsTr("Mission End") - checked: true + FactComboBox { + fact: QGroundControl.settingsManager.appSettings.offlineEditingVehicleType + indexModel: false + Layout.preferredWidth: _fieldWidth + visible: _showOfflineVehicleCombos + enabled: _enableOfflineVehicleCombos } - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: _margin - visible: missionEndHeader.checked + QGCLabel { + text: qsTr("Cruise speed") + visible: _showCruiseSpeed + Layout.fillWidth: true + } + FactTextField { + fact: QGroundControl.settingsManager.appSettings.offlineEditingCruiseSpeed + visible: _showCruiseSpeed + Layout.preferredWidth: _fieldWidth + } - QGCCheckBox { - text: qsTr("Return To Launch") - checked: missionItem.missionEndRTL - onClicked: missionItem.missionEndRTL = checked - } + QGCLabel { + text: qsTr("Hover speed") + visible: _showHoverSpeed + Layout.fillWidth: true } + FactTextField { + fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed + visible: _showHoverSpeed + Layout.preferredWidth: _fieldWidth + } + } // GridLayout + SectionHeader { + id: plannedHomePositionSection + text: qsTr("Planned Home Position") + visible: !_vehicleHasHomePosition + checked: false + } - SectionHeader { - id: vehicleInfoSectionHeader - text: qsTr("Vehicle Info") - visible: _offlineEditing && !_waypointsOnlyMode - checked: false - } + Column { + anchors.left: parent.left + anchors.right: parent.right + spacing: _margin + visible: plannedHomePositionSection.checked && !_vehicleHasHomePosition GridLayout { anchors.left: parent.left @@ -168,100 +208,29 @@ Rectangle { columnSpacing: ScreenTools.defaultFontPixelWidth rowSpacing: columnSpacing columns: 2 - visible: vehicleInfoSectionHeader.visible && vehicleInfoSectionHeader.checked - - QGCLabel { - text: _firmwareLabel - Layout.fillWidth: true - visible: _showOfflineVehicleCombos - } - FactComboBox { - fact: QGroundControl.settingsManager.appSettings.offlineEditingFirmwareType - indexModel: false - Layout.preferredWidth: _fieldWidth - visible: _showOfflineVehicleCombos - enabled: _enableOfflineVehicleCombos - } QGCLabel { - text: _vehicleLabel - Layout.fillWidth: true - visible: _showOfflineVehicleCombos - } - FactComboBox { - fact: QGroundControl.settingsManager.appSettings.offlineEditingVehicleType - indexModel: false - Layout.preferredWidth: _fieldWidth - visible: _showOfflineVehicleCombos - enabled: _enableOfflineVehicleCombos - } - - QGCLabel { - text: qsTr("Cruise speed") - visible: _showCruiseSpeed - Layout.fillWidth: true + text: qsTr("Altitude") } FactTextField { - fact: QGroundControl.settingsManager.appSettings.offlineEditingCruiseSpeed - visible: _showCruiseSpeed - Layout.preferredWidth: _fieldWidth - } - - QGCLabel { - text: qsTr("Hover speed") - visible: _showHoverSpeed + fact: missionItem.plannedHomePositionAltitude Layout.fillWidth: true } - FactTextField { - fact: QGroundControl.settingsManager.appSettings.offlineEditingHoverSpeed - visible: _showHoverSpeed - Layout.preferredWidth: _fieldWidth - } - } // GridLayout - - SectionHeader { - id: plannedHomePositionSection - text: qsTr("Planned Home Position") - visible: !_vehicleHasHomePosition - checked: false } - Column { - anchors.left: parent.left - anchors.right: parent.right - spacing: _margin - visible: plannedHomePositionSection.checked && !_vehicleHasHomePosition - - GridLayout { - anchors.left: parent.left - anchors.right: parent.right - columnSpacing: ScreenTools.defaultFontPixelWidth - rowSpacing: columnSpacing - columns: 2 - - QGCLabel { - text: qsTr("Altitude") - } - FactTextField { - fact: missionItem.plannedHomePositionAltitude - Layout.fillWidth: true - } - } - - QGCLabel { - width: parent.width - wrapMode: Text.WordWrap - font.pointSize: ScreenTools.smallFontPointSize - text: qsTr("Actual position set by vehicle at flight time.") - horizontalAlignment: Text.AlignHCenter - } + QGCLabel { + width: parent.width + wrapMode: Text.WordWrap + font.pointSize: ScreenTools.smallFontPointSize + text: qsTr("Actual position set by vehicle at flight time.") + horizontalAlignment: Text.AlignHCenter + } - QGCButton { - text: qsTr("Set Home To Map Center") - onClicked: missionItem.coordinate = map.center - anchors.horizontalCenter: parent.horizontalCenter - } + QGCButton { + text: qsTr("Set Home To Map Center") + onClicked: missionItem.coordinate = map.center + anchors.horizontalCenter: parent.horizontalCenter } - } // Column - } // Deferred loader + } + } // Column } // Rectangle diff --git a/src/PlanView/PlanToolBar.qml b/src/PlanView/PlanToolBar.qml index c37b38257c43a221c89527b69166725f194d1f36..754f3a38442f270112d9c47b376e7c8d9d75c3a9 100644 --- a/src/PlanView/PlanToolBar.qml +++ b/src/PlanView/PlanToolBar.qml @@ -32,13 +32,13 @@ Rectangle { property real missionMaxTelemetry: _controllerValid ? planMasterController.missionController.missionMaxTelemetry : NaN property bool missionDirty: _controllerValid ? planMasterController.missionController.dirty : false - property bool _controllerValid: planMasterController != undefined + property bool _controllerValid: planMasterController !== undefined property bool _controllerOffline: _controllerValid ? planMasterController.offline : true property var _controllerDirty: _controllerValid ? planMasterController.dirty : false property var _controllerSyncInProgress: _controllerValid ? planMasterController.syncInProgress : false - property bool _statusValid: currentMissionItem != undefined - property bool _missionValid: missionItems != undefined + property bool _statusValid: currentMissionItem !== undefined + property bool _missionValid: missionItems !== undefined property real _dataFontSize: ScreenTools.defaultFontPointSize property real _largeValueWidth: ScreenTools.defaultFontPixelWidth * 8 @@ -84,11 +84,8 @@ Rectangle { } //-- Eat mouse events, preventing them from reaching toolbar, which is underneath us. - MouseArea { - anchors.fill: parent - onWheel: { wheel.accepted = true; } - onPressed: { mouse.accepted = true; } - onReleased: { mouse.accepted = true; } + DeadMouseArea { + anchors.fill: parent } //-- The reason for this Row to be here is so the Logo (Home) button is in the same @@ -283,13 +280,15 @@ Rectangle { } Item { width: 1; height: 1 } - +/* + FIXME: Swap point display is currently hidden since the code which calcs it doesn't work correctly QGCLabel { text: qsTr("Swap waypoint:"); font.pointSize: _dataFontSize; } QGCLabel { text: _batteryChangePointText font.pointSize: _dataFontSize Layout.minimumWidth: _mediumValueWidth } +*/ } } diff --git a/src/PlanView/PlanView.qml b/src/PlanView/PlanView.qml index 45b57ba852ce51a646fe9abaaabe8ba115ac1223..16b4209f0a0cb4f949939d6d8052b91d5f757684 100644 --- a/src/PlanView/PlanView.qml +++ b/src/PlanView/PlanView.qml @@ -14,6 +14,7 @@ import QtQuick.Dialogs 1.2 import QtLocation 5.3 import QtPositioning 5.3 import QtQuick.Layouts 1.2 +import QtQuick.Window 2.2 import QGroundControl 1.0 import QGroundControl.FlightMap 1.0 @@ -32,13 +33,14 @@ QGCView { viewPanel: panel z: QGroundControl.zOrderTopMost - readonly property int _decimalPlaces: 8 - readonly property real _horizontalMargin: ScreenTools.defaultFontPixelWidth / 2 - readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5 - readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle - readonly property real _rightPanelWidth: Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30) - readonly property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) - readonly property var _defaultVehicleCoordinate: QtPositioning.coordinate(37.803784, -122.462276) + readonly property int _decimalPlaces: 8 + readonly property real _horizontalMargin: ScreenTools.defaultFontPixelWidth / 2 + readonly property real _margin: ScreenTools.defaultFontPixelHeight * 0.5 + readonly property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + readonly property real _rightPanelWidth: Math.min(parent.width / 3, ScreenTools.defaultFontPixelWidth * 30) + readonly property real _toolButtonTopMargin: parent.height - ScreenTools.availableHeight + (ScreenTools.defaultFontPixelHeight / 2) + readonly property var _defaultVehicleCoordinate: QtPositioning.coordinate(37.803784, -122.462276) + readonly property bool _waypointsOnlyMode: QGroundControl.corePlugin.options.missionWaypointsOnly property var _planMasterController: masterController property var _missionController: _planMasterController.missionController @@ -47,9 +49,11 @@ QGCView { property var _visualItems: _missionController.visualItems property bool _lightWidgetBorders: editorMap.isSatelliteMap property bool _addWaypointOnClick: false + property bool _addROIOnClick: false property bool _singleComplexItem: _missionController.complexMissionItemNames.length === 1 property real _toolbarHeight: _qgcView.height - ScreenTools.availableHeight property int _editingLayer: _layerMission + property int _toolStripBottom: toolStrip.height + toolStrip.y readonly property int _layerMission: 1 readonly property int _layerGeoFence: 2 @@ -219,12 +223,22 @@ QGCView { _missionController.setCurrentPlanViewIndex(sequenceNumber, true) } + /// Inserts a new ROI mission item + /// @param coordinate Location to insert item + /// @param index Insert item at this index + function insertROIMissionItem(coordinate, index) { + var sequenceNumber = _missionController.insertROIMissionItem(coordinate, index) + _missionController.setCurrentPlanViewIndex(sequenceNumber, true) + _addROIOnClick = false + toolStrip.uncheckAll() + } + property int _moveDialogMissionItemIndex QGCFileDialog { id: fileDialog qgcView: _qgcView - property var plan: true + property bool plan: true folder: QGroundControl.settingsManager.appSettings.missionSavePath fileExtension: QGroundControl.settingsManager.appSettings.planFileExtension fileExtension2: QGroundControl.settingsManager.appSettings.missionFileExtension @@ -249,7 +263,7 @@ QGCView { function accept() { var toIndex = toCombo.currentIndex - if (toIndex == 0) { + if (toIndex === 0) { toIndex = 1 } _missionController.moveMissionItem(_moveDialogMissionItemIndex, toIndex) @@ -314,6 +328,9 @@ QGCView { // than computing the coordinate offset. anchors.fill: parent onClicked: { + // Take focus to close any previous editing + editorMap.focus = true + //-- Don't pay attention to items beneath the toolbar. var topLimit = parent.height - ScreenTools.availableHeight if(mouse.y < topLimit) { @@ -329,6 +346,9 @@ QGCView { case _layerMission: if (_addWaypointOnClick) { insertSimpleMissionItem(coordinate, _missionController.visualItems.count) + } else if (_addROIOnClick) { + _addROIOnClick = false + insertROIMissionItem(coordinate, _missionController.visualItems.count) } break case _layerRallyPoints: @@ -394,11 +414,11 @@ QGCView { color: qgcPal.window title: qsTr("Plan") z: QGroundControl.zOrderWidgets - showAlternateIcon: [ false, false, masterController.dirty, false, false, false ] - rotateImage: [ false, false, masterController.syncInProgress, false, false, false ] - animateImage: [ false, false, masterController.dirty, false, false, false ] - buttonEnabled: [ true, true, !masterController.syncInProgress, true, true, true ] - buttonVisible: [ true, true, true, true, _showZoom, _showZoom ] + showAlternateIcon: [ false, false, false, masterController.dirty, false, false, false ] + rotateImage: [ false, false, false, masterController.syncInProgress, false, false, false ] + animateImage: [ false, false, false, masterController.dirty, false, false, false ] + buttonEnabled: [ true, true, true, !masterController.syncInProgress, true, true, true ] + buttonVisible: [ true, _waypointsOnlyMode, true, true, true, _showZoom, _showZoom ] maxHeight: mapScale.y - toolStrip.y property bool _showZoom: !ScreenTools.isMobile @@ -409,6 +429,11 @@ QGCView { iconSource: "/qmlimages/MapAddMission.svg", toggle: true }, + { + name: "ROI", + iconSource: "/qmlimages/MapAddMission.svg", + toggle: true + }, { name: _singleComplexItem ? _missionController.complexMissionItemNames[0] : "Pattern", iconSource: "/qmlimages/MapDrawShape.svg", @@ -439,16 +464,21 @@ QGCView { switch (index) { case 0: _addWaypointOnClick = checked + _addROIOnClick = false break case 1: + _addROIOnClick = checked + _addWaypointOnClick = false + break + case 2: if (_singleComplexItem) { addComplexItem(_missionController.complexMissionItemNames[0]) } break - case 4: + case 5: editorMap.zoomLevel += 0.5 break - case 5: + case 6: editorMap.zoomLevel -= 0.5 break } @@ -620,17 +650,18 @@ QGCView { anchors.bottom: waypointValuesDisplay.visible ? waypointValuesDisplay.top : parent.bottom anchors.left: parent.left mapControl: editorMap - visible: !ScreenTools.isTinyScreen + visible: _toolStripBottom < y } MissionItemStatus { id: waypointValuesDisplay anchors.margins: ScreenTools.defaultFontPixelWidth anchors.left: parent.left + height: ScreenTools.defaultFontPixelHeight * 7 maxWidth: parent.width - rightPanel.width - x anchors.bottom: parent.bottom missionItems: _missionController.visualItems - visible: _editingLayer === _layerMission && !ScreenTools.isShortScreen + visible: _editingLayer === _layerMission && (_toolStripBottom + mapScale.height) < y && QGroundControl.corePlugin.options.showMissionStatus } } // QGCViewPanel diff --git a/src/PlanView/RallyPointItemEditor.qml b/src/PlanView/RallyPointItemEditor.qml index 775f322a65e46c8cefbabb1cc142ddc7caa8c488..a85c84b7395e8898ab4dff84c681e420f8bb921c 100644 --- a/src/PlanView/RallyPointItemEditor.qml +++ b/src/PlanView/RallyPointItemEditor.qml @@ -17,7 +17,7 @@ Rectangle { property var rallyPoint ///< RallyPoint object associated with editor property var controller ///< RallyPointController - property bool _currentItem: rallyPoint ? rallyPoint == controller.currentRallyPoint : false + property bool _currentItem: rallyPoint ? rallyPoint === controller.currentRallyPoint : false property color _outerTextColor: _currentItem ? "black" : qgcPal.text readonly property real _margin: ScreenTools.defaultFontPixelWidth / 2 diff --git a/src/PlanView/RallyPointMapVisuals.qml b/src/PlanView/RallyPointMapVisuals.qml index 57c2f5a3d229cf678d6644f3f21094dd103f191d..e4adb46da72de7d3db54dfed21b526a9eddfdc64 100644 --- a/src/PlanView/RallyPointMapVisuals.qml +++ b/src/PlanView/RallyPointMapVisuals.qml @@ -45,7 +45,7 @@ Item { MissionItemIndicatorDrag { itemCoordinate: rallyPointObject.coordinate - visible: rallyPointObject == myRallyPointController.currentRallyPoint + visible: rallyPointObject === myRallyPointController.currentRallyPoint property var rallyPointObject @@ -67,7 +67,7 @@ Item { sourceItem: MissionItemIndexLabel { id: itemIndexLabel label: qsTr("R", "rally point map item label") - checked: _editingLayer == _layerRallyPoints ? rallyPointObject == myRallyPointController.currentRallyPoint : false + checked: _editingLayer == _layerRallyPoints ? rallyPointObject === myRallyPointController.currentRallyPoint : false onClicked: myRallyPointController.currentRallyPoint = rallyPointObject } diff --git a/src/PlanView/SectionHeader.qml b/src/PlanView/SectionHeader.qml index bcc63abe3c97a2d53707e0b33bf6119a1979d2c8..9f0b6b8a7a582e3f0d9ef43dd17607f7b6559f2d 100644 --- a/src/PlanView/SectionHeader.qml +++ b/src/PlanView/SectionHeader.qml @@ -46,8 +46,9 @@ FocusScope { } QGCLabel { - id: label - Layout.fillWidth: true + id: label + anchors.left: parent.left + anchors.right: parent.right QGCColoredImage { id: image diff --git a/src/PlanView/SimpleItemEditor.qml b/src/PlanView/SimpleItemEditor.qml index 23106868791f0a2bfe6153d04a31d1625ae946fd..75c6da4f306b64596bffc941babfa7e0cee4a3a3 100644 --- a/src/PlanView/SimpleItemEditor.qml +++ b/src/PlanView/SimpleItemEditor.qml @@ -44,7 +44,7 @@ Rectangle { QGCLabel { text: object.name - visible: object.name != "" + visible: object.name !== "" Layout.column: 0 Layout.row: index } diff --git a/src/PlanView/StructureScanEditor.qml b/src/PlanView/StructureScanEditor.qml index 0aeeae7e330d9e8381e3dcc487c2c6793d2a5d80..60079ea9d199c3404d92bd5ebc413f621a01921d 100644 --- a/src/PlanView/StructureScanEditor.qml +++ b/src/PlanView/StructureScanEditor.qml @@ -29,7 +29,6 @@ Rectangle { property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 property var _vehicle: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle : QGroundControl.multiVehicleManager.offlineEditingVehicle - function polygonCaptureStarted() { missionItem.clearPolygon() } @@ -49,11 +48,6 @@ Rectangle { QGCPalette { id: qgcPal; colorGroupEnabled: true } - ExclusiveGroup { - id: yawRadiosGroup - onCurrentChanged: missionItem.yawVehicleToStructure = yawVehicleRadio.checked - } - Column { id: editorColumn anchors.margins: _margin @@ -62,14 +56,6 @@ Rectangle { anchors.right: parent.right spacing: _margin - QGCLabel { - anchors.left: parent.left - anchors.right: parent.right - text: qsTr("WORK IN PROGRESS. CAREFUL!") - wrapMode: Text.WordWrap - color: qgcPal.warningText - } - QGCLabel { anchors.left: parent.left anchors.right: parent.right @@ -113,13 +99,27 @@ Rectangle { rowSpacing: _margin columns: 2 - QGCLabel { text: qsTr("Layers") } + QGCLabel { + text: qsTr("Structure height") + visible: !missionItem.cameraCalc.isManualCamera + } + FactTextField { + fact: missionItem.structureHeight + Layout.fillWidth: true + visible: !missionItem.cameraCalc.isManualCamera + } + + QGCLabel { + text: qsTr("# Layers") + visible: missionItem.cameraCalc.isManualCamera + } FactTextField { fact: missionItem.layers Layout.fillWidth: true + visible: missionItem.cameraCalc.isManualCamera } - QGCLabel { text: qsTr("Altitude") } + QGCLabel { text: qsTr("Bottom layer alt") } FactTextField { fact: missionItem.altitude Layout.fillWidth: true @@ -133,26 +133,9 @@ Rectangle { } } - QGCLabel { text: qsTr("Point camera to structure using:") } - - RowLayout { - spacing: _margin - - QGCRadioButton { - id: yawVehicleRadio - text: qsTr("Vehicle yaw") - exclusiveGroup: yawRadiosGroup - checked: !!missionItem.yawVehicleToStructure - enabled: false - } - - QGCRadioButton - { - text: qsTr("Gimbal yaw") - exclusiveGroup: yawRadiosGroup - checked: !missionItem.yawVehicleToStructure - enabled: false - } + Item { + height: ScreenTools.defaultFontPixelHeight / 2 + width: 1 } QGCButton { diff --git a/src/PlanView/StructureScanMapVisual.qml b/src/PlanView/StructureScanMapVisual.qml index 21f97ce10e81f111048f975f2f9c11a959a5ebc2..333c53b992738b7678c945162f12b564f6c2935e 100644 --- a/src/PlanView/StructureScanMapVisual.qml +++ b/src/PlanView/StructureScanMapVisual.qml @@ -61,9 +61,10 @@ Item { var bottomLeftCoord = map.toCoordinate(Qt.point(rect.x, rect.y + rect.height), false /* clipToViewPort */) var bottomRightCoord = map.toCoordinate(Qt.point(rect.x + rect.width, rect.y + rect.height), false /* clipToViewPort */) - // Initial polygon has max width and height of 3000 meters - var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), 3000) / 2 - var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), 3000) / 2 + // Adjust polygon to max size + var maxSize = 100 + var halfWidthMeters = Math.min(topLeftCoord.distanceTo(topRightCoord), maxSize) / 2 + var halfHeightMeters = Math.min(topLeftCoord.distanceTo(bottomLeftCoord), maxSize) / 2 topLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 0) topRightCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, 90).atDistanceAndAzimuth(halfHeightMeters, 0) bottomLeftCoord = centerCoord.atDistanceAndAzimuth(halfWidthMeters, -90).atDistanceAndAzimuth(halfHeightMeters, 180) diff --git a/src/PlanView/SurveyItemEditor.qml b/src/PlanView/SurveyItemEditor.qml index 8b438dd7421d97644d9a19e189bda3b37dce7718..ad47d11ebe470df2c7fc302f299d25c37763517c 100644 --- a/src/PlanView/SurveyItemEditor.qml +++ b/src/PlanView/SurveyItemEditor.qml @@ -46,7 +46,7 @@ Rectangle { } else { var index = -1 for (index=0; index<_cameraList.length; index++) { - if (_cameraList[index] == missionItem.camera.value) { + if (_cameraList[index] === missionItem.camera.value) { break; } } @@ -243,7 +243,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right spacing: _margin - visible: missionItem.manualGrid.value == true + visible: missionItem.manualGrid.value QGCCheckBox { id: cameraTriggerDistanceCheckBox @@ -273,7 +273,7 @@ Rectangle { anchors.left: parent.left anchors.right: parent.right spacing: _margin - visible: gridTypeCombo.currentIndex != _gridTypeManual + visible: gridTypeCombo.currentIndex !== _gridTypeManual Row { spacing: _margin @@ -409,7 +409,7 @@ Rectangle { FactCheckBox { text: qsTr("Take images in turnarounds") fact: missionItem.cameraTriggerInTurnaround - enabled: !missionItem.hoverAndCapture.rawValue + enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true } SectionHeader { @@ -443,7 +443,7 @@ Rectangle { id: windRoseButton anchors.verticalCenter: angleText.verticalCenter iconSource: qgcPal.globalTheme === QGCPalette.Light ? "/res/wind-roseBlack.svg" : "/res/wind-rose.svg" - visible: _vehicle.fixedWing + visible: _vehicle ? _vehicle.fixedWing : false onClicked: { windRosePie.angle = Number(gridAngleText.text) @@ -522,7 +522,7 @@ Rectangle { SectionHeader { id: manualGridHeader text: qsTr("Grid") - visible: gridTypeCombo.currentIndex == _gridTypeManual + visible: gridTypeCombo.currentIndex === _gridTypeManual } GridLayout { @@ -547,7 +547,7 @@ Rectangle { anchors.verticalCenter: manualAngleText.verticalCenter Layout.columnSpan: 1 iconSource: qgcPal.globalTheme === QGCPalette.Light ? "/res/wind-roseBlack.svg" : "/res/wind-rose.svg" - visible: _vehicle.fixedWing + visible: _vehicle ? _vehicle.fixedWing : false onClicked: { var cords = manualWindRoseButton.mapToItem(_root, 0, 0) @@ -605,7 +605,7 @@ Rectangle { FactCheckBox { text: qsTr("Take images in turnarounds") fact: missionItem.cameraTriggerInTurnaround - enabled: !missionItem.hoverAndCapture.rawValue + enabled: missionItem.hoverAndCaptureAllowed ? !missionItem.hoverAndCapture.rawValue : true Layout.columnSpan: 2 } diff --git a/src/PlanView/SurveyMapVisual.qml b/src/PlanView/SurveyMapVisual.qml index 08a52be380aa7cfa768bb91fd59eed983fe0a5c1..7a10c98eee99af5a7b4ab531c95f6b4793716598 100644 --- a/src/PlanView/SurveyMapVisual.qml +++ b/src/PlanView/SurveyMapVisual.qml @@ -90,6 +90,7 @@ Item { QGCMapPolygonVisuals { id: mapPolygonVisuals + qgcView: _root.qgcView mapControl: map mapPolygon: _mapPolygon interactive: _missionItem.isCurrentItem diff --git a/src/PositionManager/PositionManager.cpp b/src/PositionManager/PositionManager.cpp index 2b11994828c9ad0946dc7fb8c6869097ec21e32b..c3ad173af98fc675bcbdbe02a2a4e41c83ada27f 100644 --- a/src/PositionManager/PositionManager.cpp +++ b/src/PositionManager/PositionManager.cpp @@ -12,9 +12,12 @@ #include "QGCCorePlugin.h" QGCPositionManager::QGCPositionManager(QGCApplication* app, QGCToolbox* toolbox) - : QGCTool(app, toolbox) - , _updateInterval(0) - , _currentSource(nullptr) + : QGCTool (app, toolbox) + , _updateInterval (0) + , _currentSource (NULL) + , _defaultSource (NULL) + , _nmeaSource (NULL) + , _simulatedSource (NULL) { } @@ -22,6 +25,7 @@ QGCPositionManager::QGCPositionManager(QGCApplication* app, QGCToolbox* toolbox) QGCPositionManager::~QGCPositionManager() { delete(_simulatedSource); + delete(_nmeaSource); } void QGCPositionManager::setToolbox(QGCToolbox *toolbox) @@ -41,7 +45,17 @@ void QGCPositionManager::setToolbox(QGCToolbox *toolbox) // _defaultSource = _simulatedSource; // } - setPositionSource(QGCPositionSource::GPS); + setPositionSource(QGCPositionSource::InternalGPS); +} + +void QGCPositionManager::setNmeaSourceDevice(QIODevice* device) +{ + if (_nmeaSource) { + delete _nmeaSource; + } + _nmeaSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode, this); + _nmeaSource->setDevice(device); + setPositionSource(QGCPositionManager::NmeaGPS); } void QGCPositionManager::_positionUpdated(const QGeoPositionInfo &update) @@ -73,7 +87,10 @@ void QGCPositionManager::setPositionSource(QGCPositionManager::QGCPositionSource case QGCPositionManager::Simulated: _currentSource = _simulatedSource; break; - case QGCPositionManager::GPS: + case QGCPositionManager::NmeaGPS: + _currentSource = _nmeaSource; + break; + case QGCPositionManager::InternalGPS: default: _currentSource = _defaultSource; break; @@ -84,7 +101,6 @@ void QGCPositionManager::setPositionSource(QGCPositionManager::QGCPositionSource _currentSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods); _currentSource->setUpdateInterval(_updateInterval); connect(_currentSource, &QGeoPositionInfoSource::positionUpdated, this, &QGCPositionManager::_positionUpdated); - connect(_currentSource, &QGeoPositionInfoSource::updateTimeout, this, &QGCPositionManager::_updateTimeout); connect(_currentSource, SIGNAL(error(QGeoPositionInfoSource::Error)), this, SLOT(_error(QGeoPositionInfoSource::Error))); _currentSource->startUpdates(); } @@ -94,8 +110,3 @@ void QGCPositionManager::_error(QGeoPositionInfoSource::Error positioningError) { qWarning() << "QGCPositionManager error" << positioningError; } - -void QGCPositionManager::_updateTimeout(void) -{ - qWarning() << "QGCPositionManager updateTimeout"; -} diff --git a/src/PositionManager/PositionManager.h b/src/PositionManager/PositionManager.h index be92d4a2433a703ebc0be44b3e49fce48522c10e..c8fdadd1baec4d319ed39085a873a7c3a1ce6b63 100644 --- a/src/PositionManager/PositionManager.h +++ b/src/PositionManager/PositionManager.h @@ -10,6 +10,7 @@ #pragma once #include +#include #include @@ -26,8 +27,9 @@ public: enum QGCPositionSource { Simulated, - GPS, - Log + InternalGPS, + Log, + NmeaGPS }; void setPositionSource(QGCPositionSource source); @@ -36,10 +38,11 @@ public: void setToolbox(QGCToolbox* toolbox); + void setNmeaSourceDevice(QIODevice* device); + private slots: void _positionUpdated(const QGeoPositionInfo &update); void _error(QGeoPositionInfoSource::Error positioningError); - void _updateTimeout(void); signals: void lastPositionUpdated(bool valid, QVariant lastPosition); @@ -49,5 +52,6 @@ private: int _updateInterval; QGeoPositionInfoSource * _currentSource; QGeoPositionInfoSource * _defaultSource; + QNmeaPositionInfoSource * _nmeaSource; QGeoPositionInfoSource * _simulatedSource; }; diff --git a/src/QGC.cc b/src/QGC.cc index 812099ab6a0e0ffd2a76bfa96f2c758d9b787159..dcfa7e2d52a6064334d6bd2938a047b46889de4e 100644 --- a/src/QGC.cc +++ b/src/QGC.cc @@ -61,14 +61,14 @@ double limitAngleToPMPId(double angle) { while (angle < -M_PI) { - angle += M_PI; + angle += 2.0f * M_PI; } } else if (angle > M_PI) { while (angle > M_PI) { - angle -= M_PI; + angle -= 2.0f * M_PI; } } } diff --git a/src/QGCApplication.cc b/src/QGCApplication.cc index 724a8b54b0bf35bbc89a47c69b178b6829cdb36f..ec3d865182f5c6a34c42ab8291559ce7dfa4816f 100644 --- a/src/QGCApplication.cc +++ b/src/QGCApplication.cc @@ -83,6 +83,7 @@ #include "QGCCameraManager.h" #include "CameraCalc.h" #include "VisualMissionItem.h" +#include "EditPositionDialogController.h" #ifndef NO_SERIAL_LINK #include "SerialLink.h" @@ -310,8 +311,20 @@ QGCApplication::QGCApplication(int &argc, char* argv[], bool unitTesting) } #endif + // gstreamer debug settings + QString savePath, gstDebugLevel; + if (settings.contains(AppSettings::savePathName)) { + savePath = settings.value("SavePath").toString() + "/Logs/gst"; // hardcode log path here, appsetting is not available yet + if (!QDir(savePath).exists()) { + QDir().mkdir(savePath); + } + } + if (settings.contains(AppSettings::gstDebugName)) { + gstDebugLevel = "*:" + settings.value("GstreamerDebugLevel").toString(); + } + // Initialize Video Streaming - initializeVideoStreaming(argc, argv); + initializeVideoStreaming(argc, argv, savePath.toUtf8().data(), gstDebugLevel.toUtf8().data()); _toolbox = new QGCToolbox(this); _toolbox->setChildToolboxes(); @@ -379,6 +392,7 @@ void QGCApplication::_initCommon(void) qmlRegisterType ("QGroundControl.Controllers", 1, 0, "JoystickConfigController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "LogDownloadController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "SyslinkComponentController"); + qmlRegisterType ("QGroundControl.Controllers", 1, 0, "EditPositionDialogController"); #ifndef __mobile__ qmlRegisterType ("QGroundControl.Controllers", 1, 0, "ViewWidgetController"); qmlRegisterType ("QGroundControl.Controllers", 1, 0, "CustomCommandWidgetController"); diff --git a/src/QGCFileDownload.cc b/src/QGCFileDownload.cc index 014d5c6fada27f2738ca874e29e8167578d934e9..153f9b8d841683c54cd5b5089e2a67a1a1b37aba 100644 --- a/src/QGCFileDownload.cc +++ b/src/QGCFileDownload.cc @@ -95,7 +95,8 @@ void QGCFileDownload::_downloadFinished(void) // When an error occurs or the user cancels the download, we still end up here. So bail out in // those cases. - if (reply->error() != QNetworkReply::NoError) { + if (reply->error() != QNetworkReply::NoError) { + reply->deleteLater(); return; } @@ -128,6 +129,8 @@ void QGCFileDownload::_downloadFinished(void) qWarning() << errorMsg; emit error(errorMsg); } + + reply->deleteLater(); } /// @brief Called when an error occurs during download diff --git a/src/QGCGeo.cc b/src/QGCGeo.cc index 16eca3665144c9b5a80504f0223db6fee6d14070..0c27941cb9ce26b72b6f638854256ccb9268e68f 100644 --- a/src/QGCGeo.cc +++ b/src/QGCGeo.cc @@ -7,11 +7,13 @@ * ****************************************************************************/ +#include #include #include #include "QGCGeo.h" +#include "UTM.h" // These defines are private #define M_DEG_TO_RAD (M_PI / 180.0) @@ -26,7 +28,13 @@ static const float epsilon = std::numeric_limits::epsilon(); -void convertGeoToNed(QGeoCoordinate coord, QGeoCoordinate origin, double* x, double* y, double* z) { +void convertGeoToNed(QGeoCoordinate coord, QGeoCoordinate origin, double* x, double* y, double* z) +{ + if (coord == origin) { + // Short circuit to prevent NaNs in calculation + *x = *y = *z = 0; + return; + } double lat_rad = coord.latitude() * M_DEG_TO_RAD; double lon_rad = coord.longitude() * M_DEG_TO_RAD; @@ -81,3 +89,16 @@ void convertNedToGeo(double x, double y, double z, QGeoCoordinate origin, QGeoCo coord->setAltitude(-z + origin.altitude()); } +int convertGeoToUTM(const QGeoCoordinate& coord, double& easting, double& northing) +{ + return LatLonToUTMXY(coord.latitude(), coord.longitude(), -1 /* zone */, easting, northing); +} + +void convertUTMToGeo(double easting, double northing, int zone, bool southhemi, QGeoCoordinate& coord) +{ + double latRadians, lonRadians; + + UTMXYToLatLon (easting, northing, zone, southhemi, latRadians, lonRadians); + coord.setLatitude(RadToDeg(latRadians)); + coord.setLongitude(RadToDeg(lonRadians)); +} diff --git a/src/QGCGeo.h b/src/QGCGeo.h index 82d0e4c2492c83d7d688038a400a08b28d7ebd3f..18f91ed2cdff24e1d267d18fde9577ece26f6829 100644 --- a/src/QGCGeo.h +++ b/src/QGCGeo.h @@ -20,11 +20,6 @@ #include -/* Safeguard for systems lacking sincos (e.g. Mac OS X Leopard) */ -#ifndef sincos -#define sincos(th,x,y) { (*(x))=sin(th); (*(y))=cos(th); } -#endif - /** * @brief Project a geodetic coordinate on to local tangential plane (LTP) as coordinate with East, * North, and Down components in meters. @@ -46,4 +41,44 @@ void convertGeoToNed(QGeoCoordinate coord, QGeoCoordinate origin, double* x, dou */ void convertNedToGeo(double x, double y, double z, QGeoCoordinate origin, QGeoCoordinate *coord); +// LatLonToUTMXY +// Converts a latitude/longitude pair to x and y coordinates in the +// Universal Transverse Mercator projection. +// +// Inputs: +// lat - Latitude of the point, in radians. +// lon - Longitude of the point, in radians. +// zone - UTM zone to be used for calculating values for x and y. +// If zone is less than 1 or greater than 60, the routine +// will determine the appropriate zone from the value of lon. +// +// Outputs: +// x - The x coordinate (easting) of the computed point. (in meters) +// y - The y coordinate (northing) of the computed point. (in meters) +// +// Returns: +// The UTM zone used for calculating the values of x and y. +int convertGeoToUTM(const QGeoCoordinate& coord, double& easting, double& northing); + +// UTMXYToLatLon +// +// Converts x and y coordinates in the Universal Transverse Mercator// The UTM zone parameter should be in the range [1,60]. + +// projection to a latitude/longitude pair. +// +// Inputs: +// x - The easting of the point, in meters. +// y - The northing of the point, in meters. +// zone - The UTM zone in which the point lies. +// southhemi - True if the point is in the southern hemisphere; +// false otherwise. +// +// Outputs: +// lat - The latitude of the point, in radians. +// lon - The longitude of the point, in radians. +// +// Returns: +// The function does not return a value. +void convertUTMToGeo(double easting, double northing, int zone, bool southhemi, QGeoCoordinate& coord); + #endif // QGCGEO_H diff --git a/src/QmlControls/AppMessages.qml b/src/QmlControls/AppMessages.qml index ca82d67c3bd86f6d8d31087ec35b364e229d8a17..77cfe33dbcf87568996f245e31fb97a963e3b85c 100644 --- a/src/QmlControls/AppMessages.qml +++ b/src/QmlControls/AppMessages.qml @@ -15,6 +15,8 @@ import QtQuick.Dialogs 1.2 import QGroundControl 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 import QGroundControl.Controllers 1.0 import QGroundControl.ScreenTools 1.0 @@ -140,6 +142,24 @@ QGCView { text: qsTr("Save App Log") } + QGCLabel { + id: gstLabel + anchors.baseline: gstCombo.baseline + anchors.right: gstCombo.left + anchors.rightMargin: ScreenTools.defaultFontPixelWidth + text: "gstreamer debug level:" + } + + FactComboBox { + id: gstCombo + anchors.right: followTail.left + anchors.rightMargin: ScreenTools.defaultFontPixelWidth*20 + anchors.bottom: parent.bottom + width: ScreenTools.defaultFontPixelWidth*20 + model: ["disabled", "1", "2", "3", "4", "5", "6", "7", "8"] + fact: QGroundControl.settingsManager.appSettings.gstDebug + } + BusyIndicator { id: writeBusy anchors.bottom: writeButton.bottom diff --git a/src/QmlControls/DeadMouseArea.qml b/src/QmlControls/DeadMouseArea.qml new file mode 100644 index 0000000000000000000000000000000000000000..39db1ec18925e59f172304a93494b35bd0d7f086 --- /dev/null +++ b/src/QmlControls/DeadMouseArea.qml @@ -0,0 +1,10 @@ +import QtQuick 2.3 +import QtQuick.Controls 1.2 + +MouseArea { + preventStealing:true + hoverEnabled: true + onWheel: { wheel.accepted = true; } + onPressed: { mouse.accepted = true; } + onReleased: { mouse.accepted = true; } +} diff --git a/src/QmlControls/DropButton.qml b/src/QmlControls/DropButton.qml index ceb0cf48a81bc5aeb7934a343e2a103efedd6d3d..7815ffed7f4b211e5f60ae675592c233995817bc 100644 --- a/src/QmlControls/DropButton.qml +++ b/src/QmlControls/DropButton.qml @@ -175,9 +175,9 @@ Item { id: arrowCanvas anchors.fill: parent - property var arrowPoint: Qt.point(0, 0) - property var arrowBase1: Qt.point(0, 0) - property var arrowBase2: Qt.point(0, 0) + property point arrowPoint: Qt.point(0, 0) + property point arrowBase1: Qt.point(0, 0) + property point arrowBase2: Qt.point(0, 0) onPaint: { var context = getContext("2d") diff --git a/src/QmlControls/DropPanel.qml b/src/QmlControls/DropPanel.qml index 11c00e99ab808926b5709f4d84adfc0dac92c19e..91fe3dcc58206c681f06b75c3d2f3d36a5dab191 100644 --- a/src/QmlControls/DropPanel.qml +++ b/src/QmlControls/DropPanel.qml @@ -124,9 +124,9 @@ Item { id: arrowCanvas anchors.fill: parent - property var arrowPoint: Qt.point(0, 0) - property var arrowBase1: Qt.point(0, 0) - property var arrowBase2: Qt.point(0, 0) + property point arrowPoint: Qt.point(0, 0) + property point arrowBase1: Qt.point(0, 0) + property point arrowBase2: Qt.point(0, 0) onPaint: { var context = getContext("2d") diff --git a/src/QmlControls/EditPositionDialog.FactMetaData.json b/src/QmlControls/EditPositionDialog.FactMetaData.json new file mode 100644 index 0000000000000000000000000000000000000000..84553deec295cd58c3128c7d9971fcdd3de52102 --- /dev/null +++ b/src/QmlControls/EditPositionDialog.FactMetaData.json @@ -0,0 +1,44 @@ +[ +{ + "name": "Latitude", + "shortDescription": "Latitude of item position", + "type": "double", + "min": -90.0, + "max": 90.0, + "decimalPlaces": 7 +}, +{ + "name": "Longitude", + "shortDescription": "Longitude of item position", + "type": "double", + "min": -180.0, + "max": 180.0, + "decimalPlaces": 7 +}, +{ + "name": "Easting", + "shortDescription": "Easting of item position", + "type": "double", + "decimalPlaces": 7 +}, +{ + "name": "Northing", + "shortDescription": "Northing of item position", + "type": "double", + "decimalPlaces": 7 +}, +{ + "name": "Zone", + "shortDescription": "UTM zone", + "type": "uint8", + "min": 1, + "max": 60 +}, +{ + "name": "Hemisphere", + "shortDescription": "Hemisphere for position", + "type": "uint8", + "enumStrings": "North,South", + "enumValues": "0,1" +} +] diff --git a/src/QmlControls/EditPositionDialog.qml b/src/QmlControls/EditPositionDialog.qml new file mode 100644 index 0000000000000000000000000000000000000000..bd20d0b51578d86755150a15dac92ed2449f106b --- /dev/null +++ b/src/QmlControls/EditPositionDialog.qml @@ -0,0 +1,117 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Controllers 1.0 + +QGCViewDialog { + property alias coordinate: controller.coordinate + + property real _margin: ScreenTools.defaultFontPixelWidth / 2 + property real _fieldWidth: ScreenTools.defaultFontPixelWidth * 10.5 + + EditPositionDialogController { + id: controller + + Component.onCompleted: initValues() + } + + QGCFlickable { + anchors.fill: parent + contentHeight: column.height + + Column { + id: column + anchors.left: parent.left + anchors.right: parent.right + spacing: ScreenTools.defaultFontPixelHeight + + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + + QGCLabel { text: qsTr("Latitude") } + FactTextField { + fact: controller.latitude + Layout.fillWidth: true + } + + QGCLabel { text: qsTr("Longitude") } + FactTextField { + fact: controller.longitude + Layout.fillWidth: true + } + } + + QGCButton { + anchors.right: parent.right + text: qsTr("Set Geographic") + + onClicked: { + controller.setFromGeo() + reject() + } + } + + GridLayout { + anchors.left: parent.left + anchors.right: parent.right + columnSpacing: _margin + rowSpacing: _margin + columns: 2 + + QGCLabel { text: qsTr("Zone") } + FactTextField { + fact: controller.zone + Layout.fillWidth: true + } + + QGCLabel { text: qsTr("Hemisphere") } + FactComboBox { + fact: controller.hemisphere + indexModel: false + Layout.fillWidth: true + } + + QGCLabel { text: qsTr("Easting") } + FactTextField { + fact: controller.easting + Layout.fillWidth: true + } + + QGCLabel { text: qsTr("Northing") } + FactTextField { + fact: controller.northing + Layout.fillWidth: true + } + } + + QGCButton { + anchors.right: parent.right + text: qsTr("Set UTM") + + onClicked: { + controller.setFromUTM() + reject() + } + } + } // Column + } // QGCFlickable +} // QGCViewDialog diff --git a/src/QmlControls/EditPositionDialogController.cc b/src/QmlControls/EditPositionDialogController.cc new file mode 100644 index 0000000000000000000000000000000000000000..1ad11b5c9f05ab431684173c45011b69e7278943 --- /dev/null +++ b/src/QmlControls/EditPositionDialogController.cc @@ -0,0 +1,76 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "EditPositionDialogController.h" +#include "QGCGeo.h" + +const char* EditPositionDialogController::_latitudeFactName = "Latitude"; +const char* EditPositionDialogController::_longitudeFactName = "Longitude"; +const char* EditPositionDialogController::_zoneFactName = "Zone"; +const char* EditPositionDialogController::_hemisphereFactName = "Hemisphere"; +const char* EditPositionDialogController::_eastingFactName = "Easting"; +const char* EditPositionDialogController::_northingFactName = "Northing"; + +QMap EditPositionDialogController::_metaDataMap; + +EditPositionDialogController::EditPositionDialogController(void) + : _latitudeFact (0, _latitudeFactName, FactMetaData::valueTypeDouble) + , _longitudeFact (0, _longitudeFactName, FactMetaData::valueTypeDouble) + , _zoneFact (0, _zoneFactName, FactMetaData::valueTypeUint8) + , _hemisphereFact (0, _hemisphereFactName, FactMetaData::valueTypeUint8) + , _eastingFact (0, _eastingFactName, FactMetaData::valueTypeDouble) + , _northingFact (0, _northingFactName, FactMetaData::valueTypeDouble) +{ + if (_metaDataMap.isEmpty()) { + _metaDataMap = FactMetaData::createMapFromJsonFile(QStringLiteral(":/json/EditPositionDialog.FactMetaData.json"), NULL /* QObject parent */); + } + + _latitudeFact.setMetaData (_metaDataMap[_latitudeFactName]); + _longitudeFact.setMetaData (_metaDataMap[_longitudeFactName]); + _zoneFact.setMetaData (_metaDataMap[_zoneFactName]); + _hemisphereFact.setMetaData (_metaDataMap[_hemisphereFactName]); + _eastingFact.setMetaData (_metaDataMap[_eastingFactName]); + _northingFact.setMetaData (_metaDataMap[_northingFactName]); +} + +void EditPositionDialogController::setCoordinate(QGeoCoordinate coordinate) +{ + if (coordinate != _coordinate) { + _coordinate = coordinate; + emit coordinateChanged(coordinate); + } +} + +void EditPositionDialogController::initValues(void) +{ + _latitudeFact.setRawValue(_coordinate.latitude()); + _longitudeFact.setRawValue(_coordinate.longitude()); + + double easting, northing; + int zone = convertGeoToUTM(_coordinate, easting, northing); + _zoneFact.setRawValue(zone); + _hemisphereFact.setRawValue(_coordinate.latitude() < 0); + _eastingFact.setRawValue(easting); + _northingFact.setRawValue(northing); +} + +void EditPositionDialogController::setFromGeo(void) +{ + _coordinate.setLatitude(_latitudeFact.rawValue().toDouble()); + _coordinate.setLongitude(_longitudeFact.rawValue().toDouble()); + emit coordinateChanged(_coordinate); +} + +void EditPositionDialogController::setFromUTM(void) +{ + qDebug() << _eastingFact.rawValue().toDouble() << _northingFact.rawValue().toDouble() << _zoneFact.rawValue().toInt() << (_hemisphereFact.rawValue().toInt() == 1); + convertUTMToGeo(_eastingFact.rawValue().toDouble(), _northingFact.rawValue().toDouble(), _zoneFact.rawValue().toInt(), _hemisphereFact.rawValue().toInt() == 1, _coordinate); + qDebug() << _eastingFact.rawValue().toDouble() << _northingFact.rawValue().toDouble() << _zoneFact.rawValue().toInt() << (_hemisphereFact.rawValue().toInt() == 1) << _coordinate; + emit coordinateChanged(_coordinate); +} diff --git a/src/QmlControls/EditPositionDialogController.h b/src/QmlControls/EditPositionDialogController.h new file mode 100644 index 0000000000000000000000000000000000000000..99ecd44ddcb4ec12c3544d88fa0bcae6eb40080e --- /dev/null +++ b/src/QmlControls/EditPositionDialogController.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * + * (c) 2009-2016 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 "FactSystem.h" + +class EditPositionDialogController : public QObject +{ + Q_OBJECT + +public: + EditPositionDialogController(void); + + Q_PROPERTY(QGeoCoordinate coordinate READ coordinate WRITE setCoordinate NOTIFY coordinateChanged) + Q_PROPERTY(Fact* latitude READ latitude CONSTANT) + Q_PROPERTY(Fact* longitude READ longitude CONSTANT) + Q_PROPERTY(Fact* zone READ zone CONSTANT) + Q_PROPERTY(Fact* hemisphere READ hemisphere CONSTANT) + Q_PROPERTY(Fact* easting READ easting CONSTANT) + Q_PROPERTY(Fact* northing READ northing CONSTANT) + + QGeoCoordinate coordinate(void) const { return _coordinate; } + Fact* latitude (void) { return &_latitudeFact; } + Fact* longitude (void) { return &_longitudeFact; } + Fact* zone (void) { return &_zoneFact; } + Fact* hemisphere(void) { return &_hemisphereFact; } + Fact* easting (void) { return &_eastingFact; } + Fact* northing (void) { return &_northingFact; } + + void setCoordinate(QGeoCoordinate coordinate); + + Q_INVOKABLE void initValues(void); + Q_INVOKABLE void setFromGeo(void); + Q_INVOKABLE void setFromUTM(void); + +signals: + void coordinateChanged(QGeoCoordinate coordinate); + +private: + static QMap _metaDataMap; + + QGeoCoordinate _coordinate; + + Fact _latitudeFact; + Fact _longitudeFact; + Fact _zoneFact; + Fact _hemisphereFact; + Fact _eastingFact; + Fact _northingFact; + + static const char* _latitudeFactName; + static const char* _longitudeFactName; + static const char* _zoneFactName; + static const char* _hemisphereFactName; + static const char* _eastingFactName; + static const char* _northingFactName; +}; diff --git a/src/QmlControls/FactSliderPanel.qml b/src/QmlControls/FactSliderPanel.qml index 2e1422d36bbad5d52c9c5415a22f86b35c0baf53..98f037ca1b9f529b3aa209ea95504eabd4c78bf7 100644 --- a/src/QmlControls/FactSliderPanel.qml +++ b/src/QmlControls/FactSliderPanel.qml @@ -53,12 +53,6 @@ Column { _loadComplete = true } - QGCLabel { - id: panelLabel - text: panelTitle - font.family: ScreenTools.demiboldFontFamily - } - Column { id: sliderOuterColumn anchors.left: parent.left @@ -105,6 +99,7 @@ Column { maximumValue: max stepSize: isNaN(fact.increment) ? step : fact.increment tickmarksEnabled: true + activeFocusOnPress: true property Fact fact: controller.getParameterFact(-1, param) @@ -114,16 +109,6 @@ Column { } } - activeFocusOnPress: true - - MultiPointTouchArea { - anchors.fill: parent - - minimumTouchPoints: 1 - maximumTouchPoints: 1 - mouseEnabled: false - } - // Block wheel events MouseArea { anchors.fill: parent diff --git a/src/QmlControls/FlightModeMenu.qml b/src/QmlControls/FlightModeMenu.qml index 9b553a3951dc291673923c9c39b5cad861674c7c..c46df0acafdd4008f865225720eb2330df86d3a1 100644 --- a/src/QmlControls/FlightModeMenu.qml +++ b/src/QmlControls/FlightModeMenu.qml @@ -37,13 +37,14 @@ QGCLabel { function updateFlightModesMenu() { if (activeVehicle && activeVehicle.flightModeSetAvailable) { + var i; // Remove old menu items - for (var i = 0; i < flightModesMenuItems.length; i++) { + for (i = 0; i < flightModesMenuItems.length; i++) { flightModesMenu.removeItem(flightModesMenuItems[i]) } flightModesMenuItems.length = 0 // Add new items - for (var i = 0; i < activeVehicle.flightModes.length; i++) { + for (i = 0; i < activeVehicle.flightModes.length; i++) { var menuItem = flightModeMenuItemComponent.createObject(null, { "text": activeVehicle.flightModes[i] }) flightModesMenuItems.push(menuItem) flightModesMenu.insertItem(i, menuItem) diff --git a/src/QmlControls/MissionCommandDialog.qml b/src/QmlControls/MissionCommandDialog.qml index 5320d69f3b17b781b61a6180c233451086beafa4..03532af91f9ca3b8e9809b9182db7fa70e27a95d 100644 --- a/src/QmlControls/MissionCommandDialog.qml +++ b/src/QmlControls/MissionCommandDialog.qml @@ -68,7 +68,7 @@ QGCViewDialog { color: qgcPal.button property var mavCmdInfo: modelData - property var textColor: qgcPal.buttonText + property color textColor: qgcPal.buttonText Column { id: commandColumn diff --git a/src/QmlControls/MissionItemIndexLabel.qml b/src/QmlControls/MissionItemIndexLabel.qml index 1393a35994a0ea1c8a7883056347256e308cd0d0..5b0e662f5951b5b58600c406e5a6e7ab6b94e292 100644 --- a/src/QmlControls/MissionItemIndexLabel.qml +++ b/src/QmlControls/MissionItemIndexLabel.qml @@ -116,6 +116,9 @@ Canvas { QGCMouseArea { fillItem: parent - onClicked: parent.clicked() + onClicked: { + focus = true + parent.clicked() + } } } diff --git a/src/QmlControls/NoMouseThroughRectangle.qml b/src/QmlControls/NoMouseThroughRectangle.qml deleted file mode 100644 index 295f6e3c6d32be13989425702da3643b0638a117..0000000000000000000000000000000000000000 --- a/src/QmlControls/NoMouseThroughRectangle.qml +++ /dev/null @@ -1,14 +0,0 @@ -import QtQuick 2.3 -import QtQuick.Controls 1.2 - -/// This control is used to create a Rectangle control which does not allow mouse events to bleed through to the control -/// which is beneath it. -Rectangle { - MouseArea { - anchors.fill: parent - preventStealing: true - onWheel: { wheel.accepted = true; } - onPressed: { mouse.accepted = true; } - onReleased: { mouse.accepted = true; } - } -} diff --git a/src/QmlControls/OfflineMapButton.qml b/src/QmlControls/OfflineMapButton.qml index 8df92397e6b91b04bb14b0caeafb3d9bd1325cfa..956ce6bb3c54ba5bc3884624d353321880437cb0 100644 --- a/src/QmlControls/OfflineMapButton.qml +++ b/src/QmlControls/OfflineMapButton.qml @@ -20,6 +20,8 @@ Rectangle { border.width: _showBorder ? 1: 0 border.color: qgcPal.buttonText + property var tileSet: null + property var currentSet: null property bool checked: false property bool complete: false property alias text: nameLabel.text @@ -37,6 +39,19 @@ Rectangle { signal clicked() + property ExclusiveGroup exclusiveGroup: null + onExclusiveGroupChanged: { + if (exclusiveGroup) { + checked = false + exclusiveGroup.bindCheckable(mapButton) + } + } + onCheckedChanged: { + if(checked) { + currentSet = tileSet + } + } + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } Row { @@ -85,26 +100,53 @@ Rectangle { MouseArea { anchors.fill: parent - hoverEnabled: true + hoverEnabled: !ScreenTools.isMobile onMouseXChanged: { - _lastGlobalMouseX = ScreenTools.mouseX() - _lastGlobalMouseY = ScreenTools.mouseY() + if(!ScreenTools.isMobile) { + _lastGlobalMouseX = ScreenTools.mouseX() + _lastGlobalMouseY = ScreenTools.mouseY() + } } onMouseYChanged: { - _lastGlobalMouseX = ScreenTools.mouseX() - _lastGlobalMouseY = ScreenTools.mouseY() - } - onEntered: { _hovered = true; _forceHoverOff = false; hoverTimer.start() } - onExited: { _hovered = false; _forceHoverOff = false; hoverTimer.stop() } - onPressed: { _pressed = true; } - onReleased: { _pressed = false; } - onClicked: mapButton.clicked() + if(!ScreenTools.isMobile) { + _lastGlobalMouseX = ScreenTools.mouseX() + _lastGlobalMouseY = ScreenTools.mouseY() + } + } + onEntered: { + if(!ScreenTools.isMobile) { + _hovered = true + _forceHoverOff = false + hoverTimer.start() + } + } + onExited: { + if(!ScreenTools.isMobile) { + _hovered = false + _forceHoverOff = false + hoverTimer.stop() + } + } + onPressed: { + if(!ScreenTools.isMobile) { + _pressed = true + } + } + onReleased: { + if(!ScreenTools.isMobile) { + _pressed = false + } + } + onClicked: { + checked = true + mapButton.clicked() + } } Timer { id: hoverTimer interval: 250 - repeat: true + repeat: !ScreenTools.isMobile onTriggered: { if (_lastGlobalMouseX !== ScreenTools.mouseX() || _lastGlobalMouseY !== ScreenTools.mouseY()) { _forceHoverOff = true diff --git a/src/QmlControls/PageView.qml b/src/QmlControls/PageView.qml index 80196383c57ca0623df00631fa437cdf9047fc53..6dfdc6cb4c2944bc81a19130e471c8b5a50ea278 100644 --- a/src/QmlControls/PageView.qml +++ b/src/QmlControls/PageView.qml @@ -38,7 +38,7 @@ Rectangle { width: parent.height -(_margins * 2) sourceSize.width: width fillMode: Image.PreserveAspectFit - visible: pageWidgetLoader.item.showSettingsIcon + visible: pageWidgetLoader.item ? (pageWidgetLoader.item.showSettingsIcon ? pageWidgetLoader.item.showSettingsIcon : false) : false QGCMouseArea { fillItem: parent diff --git a/src/QmlControls/ParameterEditor.qml b/src/QmlControls/ParameterEditor.qml index 2159e6435cd0732962d90baa658cf9de1bb08ab3..c09e3546efa3aca8ea9a11f4a00057bb5a206c17 100644 --- a/src/QmlControls/ParameterEditor.qml +++ b/src/QmlControls/ParameterEditor.qml @@ -7,13 +7,10 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - -import QtQuick 2.3 -import QtQuick.Controls 1.2 -import QtQuick.Dialogs 1.2 +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 +import QtQuick.Layouts 1.2 import QGroundControl 1.0 import QGroundControl.Controls 1.0 @@ -44,6 +41,8 @@ QGCView { } } + ExclusiveGroup { id: sectionGroup } + QGCViewPanel { id: panel anchors.fill: parent @@ -158,48 +157,58 @@ QGCView { anchors.bottom: parent.bottom clip: true pixelAligned: true - contentHeight: groupedViewComponentColumn.height - contentWidth: groupedViewComponentColumn.width + contentHeight: groupedViewCategoryColumn.height flickableDirection: Flickable.VerticalFlick visible: !_searchFilter - Column { - id: groupedViewComponentColumn - spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25) + ColumnLayout { + id: groupedViewCategoryColumn + anchors.left: parent.left + anchors.right: parent.right + spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25) Repeater { - model: controller.componentIds + model: controller.categories Column { - id: componentColumn - spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25) + Layout.fillWidth: true + spacing: Math.ceil(ScreenTools.defaultFontPixelHeight * 0.25) - readonly property int componentId: modelData + readonly property string category: modelData - QGCLabel { - text: qsTr("Component #: %1").arg(componentId.toString()) - font.family: ScreenTools.demiboldFontFamily - anchors.horizontalCenter: parent.horizontalCenter + SectionHeader { + id: categoryHeader + text: category + checked: controller.currentCategory == text + exclusiveGroup: sectionGroup + + onCheckedChanged: { + if (checked) { + controller.currentCategory = category + controller.currentGroup = controller.getGroupsForCategory(category)[0] + } + } } - ExclusiveGroup { id: groupGroup } + ExclusiveGroup { id: buttonGroup } Repeater { - model: controller.getGroupsForComponent(componentId) + model: categoryHeader.checked ? controller.getGroupsForCategory(category) : 0 QGCButton { width: ScreenTools.defaultFontPixelWidth * 25 text: groupName height: _rowHeight - exclusiveGroup: setupButtonGroup + checked: controller.currentGroup == text + exclusiveGroup: buttonGroup readonly property string groupName: modelData onClicked: { checked = true - _rowWidth = 10 - controller.currentComponentId = componentId - controller.currentGroup = groupName + _rowWidth = 10 + controller.currentCategory = category + controller.currentGroup = groupName } } } @@ -244,7 +253,7 @@ QGCView { id: valueLabel width: ScreenTools.defaultFontPixelWidth * 20 color: factRow.modelFact.defaultValueAvailable ? (factRow.modelFact.valueEqualsDefault ? __qgcPal.text : __qgcPal.warningText) : __qgcPal.text - text: factRow.modelFact.enumStrings.length == 0 ? factRow.modelFact.valueString + " " + factRow.modelFact.units : factRow.modelFact.enumStringValue + text: factRow.modelFact.enumStrings.length === 0 ? factRow.modelFact.valueString + " " + factRow.modelFact.units : factRow.modelFact.enumStringValue clip: true } diff --git a/src/QmlControls/ParameterEditorController.cc b/src/QmlControls/ParameterEditorController.cc index b665750af9548ee4897a941e5eac929728246438..2f980a227f11353752ad9b97b9afff3deb6cbd85 100644 --- a/src/QmlControls/ParameterEditorController.cc +++ b/src/QmlControls/ParameterEditorController.cc @@ -27,22 +27,23 @@ /// @Brief Constructs a new ParameterEditorController Widget. This widget is used within the PX4VehicleConfig set of screens. ParameterEditorController::ParameterEditorController(void) - : _currentComponentId(_vehicle->defaultComponentId()) + : _currentCategory("Standard") // FIXME: firmware specific , _parameters(new QmlObjectListModel(this)) { - const QMap >& groupMap = _vehicle->parameterManager()->getGroupMap(); - foreach (int componentId, groupMap.keys()) { - _componentIds += QString("%1").arg(componentId); - } + const QMap >& categoryMap = _vehicle->parameterManager()->getCategoryMap(); + _categories = categoryMap.keys(); + + // Move default category to front + _categories.removeOne(_currentCategory); + _categories.prepend(_currentCategory); // Be careful about no parameters - if (groupMap.contains(_currentComponentId) && groupMap[_currentComponentId].size() != 0) { - _currentGroup = groupMap[_currentComponentId].keys()[0]; + if (categoryMap.contains(_currentCategory) && categoryMap[_currentCategory].size() != 0) { + _currentGroup = categoryMap[_currentCategory].keys()[0]; } _updateParameters(); connect(this, &ParameterEditorController::searchTextChanged, this, &ParameterEditorController::_updateParameters); - connect(this, &ParameterEditorController::currentComponentIdChanged, this, &ParameterEditorController::_updateParameters); connect(this, &ParameterEditorController::currentGroupChanged, this, &ParameterEditorController::_updateParameters); } @@ -51,29 +52,29 @@ ParameterEditorController::~ParameterEditorController() } -QStringList ParameterEditorController::getGroupsForComponent(int componentId) +QStringList ParameterEditorController::getGroupsForCategory(const QString& category) { - const QMap >& groupMap = _vehicle->parameterManager()->getGroupMap(); + const QMap >& categoryMap = _vehicle->parameterManager()->getCategoryMap(); - return groupMap[componentId].keys(); + return categoryMap[category].keys(); } -QStringList ParameterEditorController::getParametersForGroup(int componentId, QString group) +QStringList ParameterEditorController::getParametersForGroup(const QString& category, const QString& group) { - const QMap >& groupMap = _vehicle->parameterManager()->getGroupMap(); + const QMap >& categoryMap = _vehicle->parameterManager()->getCategoryMap(); - return groupMap[componentId][group]; + return categoryMap[category][group]; } -QStringList ParameterEditorController::searchParametersForComponent(int componentId, const QString& searchText, bool searchInName, bool searchInDescriptions) +QStringList ParameterEditorController::searchParameters(const QString& searchText, bool searchInName, bool searchInDescriptions) { QStringList list; - foreach(const QString ¶mName, _vehicle->parameterManager()->parameterNames(componentId)) { + foreach(const QString ¶mName, _vehicle->parameterManager()->parameterNames(_vehicle->defaultComponentId())) { if (searchText.isEmpty()) { list += paramName; } else { - Fact* fact = _vehicle->parameterManager()->getParameter(componentId, paramName); + Fact* fact = _vehicle->parameterManager()->getParameter(_vehicle->defaultComponentId(), paramName); if (searchInName && fact->name().contains(searchText, Qt::CaseInsensitive)) { list += paramName; @@ -161,9 +162,9 @@ void ParameterEditorController::_updateParameters(void) QStringList searchItems = _searchText.split(' ', QString::SkipEmptyParts); if (searchItems.isEmpty()) { - const QMap >& groupMap = _vehicle->parameterManager()->getGroupMap(); - foreach (const QString& parameter, groupMap[_currentComponentId][_currentGroup]) { - newParameterList.append(_vehicle->parameterManager()->getParameter(_currentComponentId, parameter)); + const QMap >& categoryMap = _vehicle->parameterManager()->getCategoryMap(); + foreach (const QString& parameter, categoryMap[_currentCategory][_currentGroup]) { + newParameterList.append(_vehicle->parameterManager()->getParameter(_vehicle->defaultComponentId(), parameter)); } } else { foreach(const QString ¶meter, _vehicle->parameterManager()->parameterNames(_vehicle->defaultComponentId())) { diff --git a/src/QmlControls/ParameterEditorController.h b/src/QmlControls/ParameterEditorController.h index c37e0d89e04f372d43379e4e3290123dba78c976..8b0f442fb2f6a28938eb720d3e13fe265ceb3d3d 100644 --- a/src/QmlControls/ParameterEditorController.h +++ b/src/QmlControls/ParameterEditorController.h @@ -31,14 +31,14 @@ public: ~ParameterEditorController(); Q_PROPERTY(QString searchText MEMBER _searchText NOTIFY searchTextChanged) - Q_PROPERTY(int currentComponentId MEMBER _currentComponentId NOTIFY currentComponentIdChanged) + Q_PROPERTY(QString currentCategory MEMBER _currentCategory NOTIFY currentCategoryChanged) Q_PROPERTY(QString currentGroup MEMBER _currentGroup NOTIFY currentGroupChanged) Q_PROPERTY(QmlObjectListModel* parameters MEMBER _parameters CONSTANT) - Q_PROPERTY(QVariantList componentIds MEMBER _componentIds CONSTANT) + Q_PROPERTY(QStringList categories MEMBER _categories CONSTANT) - Q_INVOKABLE QStringList getGroupsForComponent(int componentId); - Q_INVOKABLE QStringList getParametersForGroup(int componentId, QString group); - Q_INVOKABLE QStringList searchParametersForComponent(int componentId, const QString& searchText, bool searchInName=true, bool searchInDescriptions=true); + Q_INVOKABLE QStringList getGroupsForCategory(const QString& category); + Q_INVOKABLE QStringList getParametersForGroup(const QString& category, const QString& group); + Q_INVOKABLE QStringList searchParameters(const QString& searchText, bool searchInName=true, bool searchInDescriptions=true); Q_INVOKABLE void clearRCToParam(void); Q_INVOKABLE void saveToFile(const QString& filename); @@ -51,7 +51,7 @@ public: signals: void searchTextChanged(QString searchText); - void currentComponentIdChanged(int componentId); + void currentCategoryChanged(QString category); void currentGroupChanged(QString group); void showErrorMessage(const QString& errorMsg); @@ -59,9 +59,9 @@ private slots: void _updateParameters(void); private: - QVariantList _componentIds; + QStringList _categories; QString _searchText; - int _currentComponentId; + QString _currentCategory; QString _currentGroup; QmlObjectListModel* _parameters; }; diff --git a/src/QmlControls/ParameterEditorDialog.qml b/src/QmlControls/ParameterEditorDialog.qml index 3a1f5e436ab24f1d89eb8ed53022952a2023b3de..4a97a174dcc4dbef0a1254f67f63f10028ad9236 100644 --- a/src/QmlControls/ParameterEditorDialog.qml +++ b/src/QmlControls/ParameterEditorDialog.qml @@ -116,7 +116,7 @@ QGCViewDialog { QGCTextField { id: valueField text: validate ? validateValue : fact.valueString - visible: fact.enumStrings.length == 0 || validate || manualEntry.checked + visible: fact.enumStrings.length === 0 || validate || manualEntry.checked unitsLabel: fact.units showUnits: fact.units != "" Layout.fillWidth: true @@ -145,7 +145,7 @@ QGCViewDialog { visible: _showCombo model: fact.enumStrings - property bool _showCombo: fact.enumStrings.length != 0 && fact.bitmaskStrings.length == 0 && !validate + property bool _showCombo: fact.enumStrings.length !== 0 && fact.bitmaskStrings.length === 0 && !validate Component.onCompleted: { // We can't bind directly to fact.enumIndex since that would add an unknown value diff --git a/src/QmlControls/QGCButton.qml b/src/QmlControls/QGCButton.qml index f9cbe883583a6acd69dcd319a30a09633bb89f35..f7adf355b18b4908e3a15c49410a2dd183191727 100644 --- a/src/QmlControls/QGCButton.qml +++ b/src/QmlControls/QGCButton.qml @@ -6,6 +6,8 @@ import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 Button { + activeFocusOnPress: true + property bool primary: false ///< primary button for a group of buttons property real pointSize: ScreenTools.defaultFontPointSize ///< Point size for button text diff --git a/src/QmlControls/QGCCheckBox.qml b/src/QmlControls/QGCCheckBox.qml index 735138d28ec87d8a44f310cc35b72e0217c94054..ed602c1678742f9bf8b8f5fb08e9f0e6def34772 100644 --- a/src/QmlControls/QGCCheckBox.qml +++ b/src/QmlControls/QGCCheckBox.qml @@ -6,6 +6,8 @@ import QGroundControl.Palette 1.0 import QGroundControl.ScreenTools 1.0 CheckBox { + activeFocusOnPress: true + property var __qgcPal: QGCPalette { colorGroupEnabled: enabled } style: CheckBoxStyle { diff --git a/src/QmlControls/QGCFileDialog.qml b/src/QmlControls/QGCFileDialog.qml index 3ab7fce5353e64fb122b5e5b288b76af2bdb129d..ecd0782ca9a2b8bdf997e3832c328266f2d8d852 100644 --- a/src/QmlControls/QGCFileDialog.qml +++ b/src/QmlControls/QGCFileDialog.qml @@ -17,29 +17,33 @@ Item { property var qgcView property string folder property var nameFilters - property string fileExtension // Primary file extension to search for - property string fileExtension2 // Secondary file extension to search for + property string fileExtension // Primary file extension to search for + property string fileExtension2: "" // Secondary file extension to search for property string title property bool selectExisting property bool selectFolder property bool _openForLoad: true property real _margins: ScreenTools.defaultFontPixelHeight / 2 - property bool _mobile: ScreenTools.isMobile + property bool _mobileDlg: QGroundControl.corePlugin.options.useMobileFileDialog property var _rgExtensions - Component.onCompleted: { - if (fileExtension2 === "") { + Component.onCompleted: setupFileExtensions() + + onFileExtensionChanged: setupFileExtensions() + onFileExtension2Changed: setupFileExtensions() + + function setupFileExtensions() { + if (fileExtension2 == "") { _rgExtensions = [ fileExtension ] } else { _rgExtensions = [ fileExtension, fileExtension2 ] - } } function openForLoad() { _openForLoad = true - if (_mobile && folder.length !== 0) { + if (_mobileDlg && folder.length !== 0) { qgcView.showDialog(mobileFileOpenDialog, title, qgcView.showDialogDefaultWidth, StandardButton.Cancel) } else { fullFileDialog.open() @@ -48,7 +52,7 @@ Item { function openForSave() { _openForLoad = false - if (_mobile && folder.length !== 0) { + if (_mobileDlg && folder.length !== 0) { qgcView.showDialog(mobileFileSaveDialog, title, qgcView.showDialogDefaultWidth, StandardButton.Cancel | StandardButton.Ok) } else { fullFileDialog.open() @@ -128,10 +132,7 @@ Item { property string fileToDelete - onAboutToHide: { - fileButton.highlight = false - hideDialog() - } + onAboutToHide: fileButton.highlight = false MenuItem { text: qsTr("Delete") @@ -143,7 +144,7 @@ Item { QGCLabel { text: qsTr("No files") - visible: fileList.model.length == 0 && fileList2.model.length == 0 + visible: fileList.model.length === 0 } } } diff --git a/src/QmlControls/QGCFlickableHorizontalIndicator.qml b/src/QmlControls/QGCFlickableHorizontalIndicator.qml index b1ac454f77b018ea8cad3808b10ab5959936daa0..41f89653a1b9a6ebaa15bbd79dfcc998ba7f8751 100644 --- a/src/QmlControls/QGCFlickableHorizontalIndicator.qml +++ b/src/QmlControls/QGCFlickableHorizontalIndicator.qml @@ -11,9 +11,9 @@ Rectangle { color: parent.indicatorColor visible: showIndicator - property bool showIndicator: (parent.flickableDirection == Flickable.AutoFlickDirection || - parent.flickableDirection == Flickable.HorizontalFlick || - parent.flickableDirection == Flickable.HorizontalAndVerticalFlick) && + property bool showIndicator: (parent.flickableDirection === Flickable.AutoFlickDirection || + parent.flickableDirection === Flickable.HorizontalFlick || + parent.flickableDirection === Flickable.HorizontalAndVerticalFlick) && (parent.contentWidth > parent.width) Component.onCompleted: animateOpacity.restart() diff --git a/src/QmlControls/QGCFlickableVerticalIndicator.qml b/src/QmlControls/QGCFlickableVerticalIndicator.qml index 67f220d6bef7e9b452c6fc2108887a0cb2e8e81c..9ab426f437718f59c15982c385df03197983ce24 100644 --- a/src/QmlControls/QGCFlickableVerticalIndicator.qml +++ b/src/QmlControls/QGCFlickableVerticalIndicator.qml @@ -11,9 +11,9 @@ Rectangle { color: parent.indicatorColor visible: showIndicator - property bool showIndicator: (parent.flickableDirection == Flickable.AutoFlickDirection || - parent.flickableDirection == Flickable.VerticalFlick || - parent.flickableDirection == Flickable.HorizontalAndVerticalFlick) && + property bool showIndicator: (parent.flickableDirection === Flickable.AutoFlickDirection || + parent.flickableDirection === Flickable.VerticalFlick || + parent.flickableDirection === Flickable.HorizontalAndVerticalFlick) && (parent.contentHeight > parent.height) Component.onCompleted: animateOpacity.restart() diff --git a/src/QmlControls/QGCPipable.qml b/src/QmlControls/QGCPipable.qml index 3a84d47b81306bd46bf9c2a69002b88a4900e18e..e832eda65969147491ec966b9eb4c51db1725da3 100644 --- a/src/QmlControls/QGCPipable.qml +++ b/src/QmlControls/QGCPipable.qml @@ -26,9 +26,13 @@ Item { property real maxSize: 0.75 property real minSize: 0.10 + property bool inPopup: false + property bool enablePopup: true + signal activated() signal hideIt(bool state) signal newWidth(real newWidth) + signal popup() MouseArea { id: pipMouseArea @@ -47,8 +51,8 @@ Item { anchors.right: parent.right height: ScreenTools.minTouchPixels width: height - property var initialX: 0 - property var initialWidth: 0 + property real initialX: 0 + property real initialWidth: 0 onClicked: { // TODO propagate @@ -87,14 +91,14 @@ Item { mipmap: true anchors.right: parent.right anchors.top: parent.top - visible: !isHidden && (ScreenTools.isMobile || pipMouseArea.containsMouse) + visible: !isHidden && (ScreenTools.isMobile || pipMouseArea.containsMouse) && !inPopup height: ScreenTools.defaultFontPixelHeight * 2.5 width: ScreenTools.defaultFontPixelHeight * 2.5 sourceSize.height: height } // Resize pip window if necessary when main window is resized - property var pipLock: 2 + property int pipLock: 2 Connections { target: pip.parent @@ -116,10 +120,31 @@ Item { } } + //-- PIP Popup Indicator + Image { + id: popupPIP + source: "/qmlimages/PiP.svg" + mipmap: true + fillMode: Image.PreserveAspectFit + anchors.left: parent.left + anchors.top: parent.top + visible: !isHidden && !inPopup && !ScreenTools.isMobile && enablePopup && pipMouseArea.containsMouse + height: ScreenTools.defaultFontPixelHeight * 2.5 + width: ScreenTools.defaultFontPixelHeight * 2.5 + sourceSize.height: height + MouseArea { + anchors.fill: parent + onClicked: { + inPopup = true + pip.popup() + } + } + } + //-- PIP Corner Indicator Image { id: closePIP - source: "/qmlimages/PiP.svg" + source: "/qmlimages/pipHide.svg" mipmap: true fillMode: Image.PreserveAspectFit anchors.left: parent.left diff --git a/src/QmlControls/QGCSlider.qml b/src/QmlControls/QGCSlider.qml index ff317b12b4a90daf8b5518baf34ab6805a675a73..5c0d427ab75a83c0ea756ae6179c2b3d99502ec7 100644 --- a/src/QmlControls/QGCSlider.qml +++ b/src/QmlControls/QGCSlider.qml @@ -21,6 +21,7 @@ Slider { // Value indicator starts display from zero instead of min value property bool zeroCentered: false + property bool displayValue: false QGCPalette { id: qgcPal; colorGroupEnabled: enabled } @@ -67,8 +68,15 @@ Slider { implicitWidth: _radius * 2 implicitHeight: _radius * 2 radius: _radius - property real _radius: Math.round(ScreenTools.defaultFontPixelHeight * 0.75) + Label { + text: _root.value.toFixed(0) + visible: _root.displayValue + anchors.centerIn: parent + font.family: ScreenTools.normalFontFamily + font.pointSize: ScreenTools.smallFontPointSize + color: qgcPal.buttonText + } } } } diff --git a/src/QmlControls/QGCSwitch.qml b/src/QmlControls/QGCSwitch.qml new file mode 100644 index 0000000000000000000000000000000000000000..74da42b1306b40dca49bf8d1354fb5a39b589d23 --- /dev/null +++ b/src/QmlControls/QGCSwitch.qml @@ -0,0 +1,30 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Controls.Styles 1.4 + +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Switch { + id: _root + QGCPalette { id:qgcPal; colorGroupEnabled: true } + style: SwitchStyle { + groove: Rectangle { + implicitWidth: ScreenTools.defaultFontPixelWidth * 6 + implicitHeight: ScreenTools.defaultFontPixelHeight + color: (control.checked && control.enabled) ? qgcPal.colorGreen : qgcPal.colorGrey + radius: 3 + border.color: qgcPal.button + border.width: 1 + } + } +} diff --git a/src/QmlControls/QGCView.qml b/src/QmlControls/QGCView.qml index 3bced362f76dc34fc98a07b193e9b3b55b2f9a73..b50ac571e77367df7375af4404b21f47676c0939 100644 --- a/src/QmlControls/QGCView.qml +++ b/src/QmlControls/QGCView.qml @@ -26,75 +26,12 @@ FactPanel { property var qgcView: _rootItem ///< Used by Fact controls for validation dialogs property bool completedSignalled: false - property real topDialogMargin: 0 ///< Set a top margin for dialog property var viewPanel /// This is signalled when the top level Item reaches Component.onCompleted. This allows /// the view subcomponent to connect to this signal and do work once the full ui is ready /// to go. - signal completed - - function _setupDialogButtons(buttons) { - _acceptButton.visible = false - _rejectButton.visible = false - - // Accept role buttons - if (buttons & StandardButton.Ok) { - _acceptButton.text = qsTr("Ok") - _acceptButton.visible = true - } else if (buttons & StandardButton.Open) { - _acceptButton.text = qsTr("Open") - _acceptButton.visible = true - } else if (buttons & StandardButton.Save) { - _acceptButton.text = qsTr("Save") - _acceptButton.visible = true - } else if (buttons & StandardButton.Apply) { - _acceptButton.text = qsTr("Apply") - _acceptButton.visible = true - } else if (buttons & StandardButton.Open) { - _acceptButton.text = qsTr("Open") - _acceptButton.visible = true - } else if (buttons & StandardButton.SaveAll) { - _acceptButton.text = qsTr("Save All") - _acceptButton.visible = true - } else if (buttons & StandardButton.Yes) { - _acceptButton.text = qsTr("Yes") - _acceptButton.visible = true - } else if (buttons & StandardButton.YesToAll) { - _acceptButton.text = qsTr("Yes to All") - _acceptButton.visible = true - } else if (buttons & StandardButton.Retry) { - _acceptButton.text = qsTr("Retry") - _acceptButton.visible = true - } else if (buttons & StandardButton.Reset) { - _acceptButton.text = qsTr("Reset") - _acceptButton.visible = true - } else if (buttons & StandardButton.RestoreToDefaults) { - _acceptButton.text = qsTr("Restore to Defaults") - _acceptButton.visible = true - } else if (buttons & StandardButton.Ignore) { - _acceptButton.text = qsTr("Ignore") - _acceptButton.visible = true - } - - // Reject role buttons - if (buttons & StandardButton.Cancel) { - _rejectButton.text = qsTr("Cancel") - _rejectButton.visible = true - } else if (buttons & StandardButton.Close) { - _rejectButton.text = qsTr("Close") - _rejectButton.visible = true - } else if (buttons & StandardButton.No) { - _rejectButton.text = qsTr("No") - _rejectButton.visible = true - } else if (buttons & StandardButton.NoToAll) { - _rejectButton.text = qsTr("No to All") - _rejectButton.visible = true - } else if (buttons & StandardButton.Abort) { - _rejectButton.text = qsTr("Abort") - _rejectButton.visible = true - } - } + signal completed function _checkForEarlyDialog(title) { if (!completedSignalled) { @@ -116,60 +53,36 @@ FactPanel { return } - _rejectButton.enabled = true - _acceptButton.enabled = true - - _dialogCharWidth = charWidth - _dialogTitle = title - - _setupDialogButtons(buttons) - - _dialogComponent = component - viewPanel.enabled = false - _dialogOverlay.visible = true - } - - function showMessage(title, message, buttons) { - if (_checkForEarlyDialog(title)) { + var dialogComponent = Qt.createComponent("QGCViewDialogContainer.qml") + if (dialogComponent.status === Component.Error) { + console.log("Error loading QGCViewDialogContainer.qml: ", dialogComponent.errorString()) return } - - _rejectButton.enabled = true - _acceptButton.enabled = true - - _dialogCharWidth = showDialogDefaultWidth - _dialogTitle = title - _messageDialogText = message - - _setupDialogButtons(buttons) - - _dialogComponent = _messageDialog + var dialogWidth = charWidth === showDialogFullWidth ? parent.width : ScreenTools.defaultFontPixelWidth * charWidth + var dialog = dialogComponent.createObject(_rootItem, + { + "anchors.fill": _rootItem, + "dialogWidth": dialogWidth, + "dialogTitle": title, + "dialogComponent": component, + "viewPanel": viewPanel + }) + dialog.setupDialogButtons(buttons) viewPanel.enabled = false - _dialogOverlay.visible = true } - function hideDialog() { - viewPanel.enabled = true - _dialogComponent = null - _dialogOverlay.visible = false + function showMessage(title, message, buttons) { + _messageDialogText = message + showDialog(_messageDialog, title, showDialogDefaultWidth, buttons) } QGCPalette { id: _qgcPal; colorGroupEnabled: true } - QGCLabel { id: _textMeasure; text: "X"; visible: false } - property real defaultTextHeight: _textMeasure.contentHeight - property real defaultTextWidth: _textMeasure.contentWidth - - /// The width of the dialog panel in characters - property int _dialogCharWidth: 75 - - /// The title for the dialog panel - property string _dialogTitle + property real defaultTextWidth: ScreenTools.defaultFontPixelWidth + property real defaultTextHeight: ScreenTools.defaultFontPixelHeight property string _messageDialogText - property Component _dialogComponent - function _signalCompleted() { // When we use this control inside a QGCQmlWidgetHolder Component.onCompleted is signalled // before the width and height are adjusted. So we need to wait for width and heigth to be @@ -184,109 +97,6 @@ FactPanel { onWidthChanged: _signalCompleted() onHeightChanged: _signalCompleted() - Connections { - target: _dialogComponentLoader.item - - onHideDialog: _rootItem.hideDialog() - } - - Item { - id: _dialogOverlay - visible: false - anchors.fill: parent - z: 5000 - - // This covers the parent with an transparent section - Rectangle { - id: _transparentSection - height: ScreenTools.availableHeight ? ScreenTools.availableHeight : parent.height - anchors.bottom: parent.bottom - anchors.left: parent.left - anchors.right: _dialogPanel.left - opacity: 0.0 - color: _qgcPal.window - } - - // This is the main dialog panel which is anchored to the right edge - Rectangle { - id: _dialogPanel - width: _dialogCharWidth == showDialogFullWidth ? parent.width : defaultTextWidth * _dialogCharWidth - anchors.topMargin: topDialogMargin - height: ScreenTools.availableHeight ? ScreenTools.availableHeight : parent.height - anchors.bottom: parent.bottom - anchors.right: parent.right - color: _qgcPal.windowShadeDark - - Rectangle { - id: _header - width: parent.width - height: _acceptButton.visible ? _acceptButton.height : _rejectButton.height - color: _qgcPal.windowShade - - function _hidePanel() { - _fullPanel.visible = false - } - - QGCLabel { - x: defaultTextWidth - height: parent.height - verticalAlignment: Text.AlignVCenter - text: _dialogTitle - } - - QGCButton { - id: _rejectButton - anchors.right: _acceptButton.visible ? _acceptButton.left : parent.right - anchors.bottom: parent.bottom - - onClicked: { - enabled = false // prevent multiple clicks - _dialogComponentLoader.item.reject() - if (!viewPanel.enabled) { - // Dialog was not closed, re-enable button - enabled = true - } - } - } - - QGCButton { - id: _acceptButton - anchors.right: parent.right - primary: true - - onClicked: { - enabled = false // prevent multiple clicks - _dialogComponentLoader.item.accept() - if (!viewPanel.enabled) { - // Dialog was not closed, re-enable button - enabled = true - } - } - } - } - - Item { - id: _spacer - width: 10 - height: 10 - anchors.top: _header.bottom - } - - Loader { - id: _dialogComponentLoader - anchors.margins: 5 - anchors.left: parent.left - anchors.right: parent.right - anchors.top: _spacer.bottom - anchors.bottom: parent.bottom - sourceComponent: _dialogComponent - - property bool acceptAllowed: _acceptButton.visible - property bool rejectAllowed: _rejectButton.visible - } - } // Rectangle - Dialog panel - } // Item - Dialog overlay - Component { id: _messageDialog diff --git a/src/QmlControls/QGCViewDialog.qml b/src/QmlControls/QGCViewDialog.qml index 5e6b7044d1efb74456fca7e627b408fc04817886..0c1e02b1c23ad98b26bc6d6daa8bc78a2b1b63e1 100644 --- a/src/QmlControls/QGCViewDialog.qml +++ b/src/QmlControls/QGCViewDialog.qml @@ -7,31 +7,30 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - -import QtQuick 2.3 +import QtQuick 2.3 import QtQuick.Controls 1.2 -import QGroundControl.Controls 1.0 -import QGroundControl.Palette 1.0 - -import QGroundControl.FactSystem 1.0 -import QGroundControl.FactControls 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.FactSystem 1.0 +import QGroundControl.FactControls 1.0 +import QGroundControl.ScreenTools 1.0 FactPanel { property var qgcTextFieldforwardKeysTo: this ///< Causes all QGCTextFields to forward keys here if they have focus + property real defaultTextWidth: ScreenTools.defaultFontPixelWidth + property real defaultTextHeight: ScreenTools.defaultFontPixelHeight + QGCPalette { id: __qgcPal; colorGroupEnabled: enabled } signal hideDialog Keys.onReleased: { - if (event.key == Qt.Key_Escape) { + if (event.key === Qt.Key_Escape) { reject() event.accepted = true - } else if (event.key == Qt.Key_Return || event.key == Qt.Key_Enter) { + } else if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { accept() event.accepted = true } diff --git a/src/QmlControls/QGCViewDialogContainer.qml b/src/QmlControls/QGCViewDialogContainer.qml new file mode 100644 index 0000000000000000000000000000000000000000..dda82095d2e11763365cad8d9cb7d3e6790e937b --- /dev/null +++ b/src/QmlControls/QGCViewDialogContainer.qml @@ -0,0 +1,190 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Dialogs 1.2 + +import QGroundControl.Controls 1.0 +import QGroundControl.Palette 1.0 +import QGroundControl.ScreenTools 1.0 + +Item { + id: _root + z: 5000 + + property alias dialogWidth: _dialogPanel.width + property alias dialogTitle: titleLabel.text + property alias dialogComponent: _dialogComponentLoader.sourceComponent + property var viewPanel + + property real _defaultTextHeight: _textMeasure.contentHeight + property real _defaultTextWidth: _textMeasure.contentWidth + + function setupDialogButtons(buttons) { + _acceptButton.visible = false + _rejectButton.visible = false + + // Accept role buttons + if (buttons & StandardButton.Ok) { + _acceptButton.text = qsTr("Ok") + _acceptButton.visible = true + } else if (buttons & StandardButton.Open) { + _acceptButton.text = qsTr("Open") + _acceptButton.visible = true + } else if (buttons & StandardButton.Save) { + _acceptButton.text = qsTr("Save") + _acceptButton.visible = true + } else if (buttons & StandardButton.Apply) { + _acceptButton.text = qsTr("Apply") + _acceptButton.visible = true + } else if (buttons & StandardButton.Open) { + _acceptButton.text = qsTr("Open") + _acceptButton.visible = true + } else if (buttons & StandardButton.SaveAll) { + _acceptButton.text = qsTr("Save All") + _acceptButton.visible = true + } else if (buttons & StandardButton.Yes) { + _acceptButton.text = qsTr("Yes") + _acceptButton.visible = true + } else if (buttons & StandardButton.YesToAll) { + _acceptButton.text = qsTr("Yes to All") + _acceptButton.visible = true + } else if (buttons & StandardButton.Retry) { + _acceptButton.text = qsTr("Retry") + _acceptButton.visible = true + } else if (buttons & StandardButton.Reset) { + _acceptButton.text = qsTr("Reset") + _acceptButton.visible = true + } else if (buttons & StandardButton.RestoreToDefaults) { + _acceptButton.text = qsTr("Restore to Defaults") + _acceptButton.visible = true + } else if (buttons & StandardButton.Ignore) { + _acceptButton.text = qsTr("Ignore") + _acceptButton.visible = true + } + + // Reject role buttons + if (buttons & StandardButton.Cancel) { + _rejectButton.text = qsTr("Cancel") + _rejectButton.visible = true + } else if (buttons & StandardButton.Close) { + _rejectButton.text = qsTr("Close") + _rejectButton.visible = true + } else if (buttons & StandardButton.No) { + _rejectButton.text = qsTr("No") + _rejectButton.visible = true + } else if (buttons & StandardButton.NoToAll) { + _rejectButton.text = qsTr("No to All") + _rejectButton.visible = true + } else if (buttons & StandardButton.Abort) { + _rejectButton.text = qsTr("Abort") + _rejectButton.visible = true + } + } + + Connections { + target: _dialogComponentLoader.item + + onHideDialog: { + viewPanel.enabled = true + _root.destroy() + } + } + + QGCPalette { id: _qgcPal; colorGroupEnabled: true } + QGCLabel { id: _textMeasure; text: "X"; visible: false } + + Rectangle { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.left: parent.left + anchors.right: _dialogPanel.left + opacity: 0.5 + color: _qgcPal.window + z: 5000 + } + + // This is the main dialog panel which is anchored to the right edge + Rectangle { + id: _dialogPanel + height: ScreenTools.availableHeight ? ScreenTools.availableHeight : parent.height + anchors.bottom: parent.bottom + anchors.right: parent.right + color: _qgcPal.windowShadeDark + + Rectangle { + id: _header + width: parent.width + height: _acceptButton.visible ? _acceptButton.height : _rejectButton.height + color: _qgcPal.windowShade + + function _hidePanel() { + _fullPanel.visible = false + } + + QGCLabel { + id: titleLabel + x: _defaultTextWidth + height: parent.height + verticalAlignment: Text.AlignVCenter + } + + QGCButton { + id: _rejectButton + anchors.right: _acceptButton.visible ? _acceptButton.left : parent.right + anchors.bottom: parent.bottom + + onClicked: { + enabled = false // prevent multiple clicks + _dialogComponentLoader.item.reject() + if (!viewPanel.enabled) { + // Dialog was not closed, re-enable button + enabled = true + } + } + } + + QGCButton { + id: _acceptButton + anchors.right: parent.right + primary: true + + onClicked: { + enabled = false // prevent multiple clicks + _dialogComponentLoader.item.accept() + if (!viewPanel.enabled) { + // Dialog was not closed, re-enable button + enabled = true + } + } + } + } + + Item { + id: _spacer + width: 10 + height: 10 + anchors.top: _header.bottom + } + + Loader { + id: _dialogComponentLoader + anchors.margins: 5 + anchors.left: parent.left + anchors.right: parent.right + anchors.top: _spacer.bottom + anchors.bottom: parent.bottom + sourceComponent: _dialogComponent + + property bool acceptAllowed: _acceptButton.visible + property bool rejectAllowed: _rejectButton.visible + } + } // Rectangle - Dialog panel +} diff --git a/src/QmlControls/QGroundControl.Controls.qmldir b/src/QmlControls/QGroundControl.Controls.qmldir index ad49c42db7e48ea25142897cd184e6ab7a540404..65b86d035be9ab5cd631f4990a6c045c8b953843 100644 --- a/src/QmlControls/QGroundControl.Controls.qmldir +++ b/src/QmlControls/QGroundControl.Controls.qmldir @@ -5,8 +5,10 @@ AppMessages 1.0 AppMessages.qml CameraCalc 1.0 CameraCalc.qml CameraSection 1.0 CameraSection.qml ClickableColor 1.0 ClickableColor.qml +DeadMouseArea 1.0 DeadMouseArea.qml DropButton 1.0 DropButton.qml DropPanel 1.0 DropPanel.qml +EditPositionDialog 1.0 EditPositionDialog.qml ExclusiveGroupItem 1.0 ExclusiveGroupItem.qml FactSliderPanel 1.0 FactSliderPanel.qml FileButton 1.0 FileButton.qml @@ -26,15 +28,11 @@ MissionItemMapVisual 1.0 MissionItemMapVisual.qml MissionItemStatus 1.0 MissionItemStatus.qml ModeSwitchDisplay 1.0 ModeSwitchDisplay.qml MultiRotorMotorDisplay 1.0 MultiRotorMotorDisplay.qml -NoMouseThroughRectangle 1.0 NoMouseThroughRectangle.qml +OfflineMapButton 1.0 OfflineMapButton.qml PageView 1.0 PageView.qml ParameterEditor 1.0 ParameterEditor.qml ParameterEditorDialog 1.0 ParameterEditorDialog.qml PlanToolBar 1.0 PlanToolBar.qml -RallyPointEditorHeader 1.0 RallyPointEditorHeader.qml -RallyPointItemEditor 1.0 RallyPointItemEditor.qml -RallyPointMapVisuals 1.0 RallyPointMapVisuals.qml -RCChannelMonitor 1.0 RCChannelMonitor.qml QGCButton 1.0 QGCButton.qml QGCCheckBox 1.0 QGCCheckBox.qml QGCColoredImage 1.0 QGCColoredImage.qml @@ -44,20 +42,27 @@ QGCFlickable 1.0 QGCFlickable.qml QGCGroupBox 1.0 QGCGroupBox.qml QGCLabel 1.0 QGCLabel.qml QGCListView 1.0 QGCListView.qml -QGCMapLabel 1.0 QGCMapLabel.qml QGCMapCircleVisuals 1.0 QGCMapCircleVisuals.qml +QGCMapLabel 1.0 QGCMapLabel.qml QGCMapPolygonVisuals 1.0 QGCMapPolygonVisuals.qml +QGCMapPolylineVisuals 1.0 QGCMapPolylineVisuals.qml QGCMouseArea 1.0 QGCMouseArea.qml QGCMovableItem 1.0 QGCMovableItem.qml QGCPipable 1.0 QGCPipable.qml QGCRadioButton 1.0 QGCRadioButton.qml QGCSlider 1.0 QGCSlider.qml +QGCSwitch 1.0 QGCSwitch.qml QGCTextField 1.0 QGCTextField.qml QGCToolBarButton 1.0 QGCToolBarButton.qml QGCView 1.0 QGCView.qml QGCViewDialog 1.0 QGCViewDialog.qml +QGCViewDialogContainer 1.0 QGCViewDialogContainer.qml QGCViewMessage 1.0 QGCViewMessage.qml QGCViewPanel 1.0 QGCViewPanel.qml +RallyPointEditorHeader 1.0 RallyPointEditorHeader.qml +RallyPointItemEditor 1.0 RallyPointItemEditor.qml +RallyPointMapVisuals 1.0 RallyPointMapVisuals.qml +RCChannelMonitor 1.0 RCChannelMonitor.qml RoundButton 1.0 RoundButton.qml SectionHeader 1.0 SectionHeader.qml SetupPage 1.0 SetupPage.qml @@ -70,4 +75,3 @@ ToolStrip 1.0 ToolStrip.qml VehicleRotationCal 1.0 VehicleRotationCal.qml VehicleSummaryRow 1.0 VehicleSummaryRow.qml ViewWidget 1.0 ViewWidget.qml -OfflineMapButton 1.0 OfflineMapButton.qml diff --git a/src/QmlControls/QGroundControlQmlGlobal.cc b/src/QmlControls/QGroundControlQmlGlobal.cc index b4875992b1bbec8f7b60f19afab7db022c3e07d1..40c5b65273933b59498fb01baae68f03007b1426 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.cc +++ b/src/QmlControls/QGroundControlQmlGlobal.cc @@ -24,6 +24,9 @@ const char* QGroundControlQmlGlobal::_flightMapPositionLatitudeSettingsKey = const char* QGroundControlQmlGlobal::_flightMapPositionLongitudeSettingsKey = "Longitude"; const char* QGroundControlQmlGlobal::_flightMapZoomSettingsKey = "FlightMapZoom"; +QGeoCoordinate QGroundControlQmlGlobal::_coord = QGeoCoordinate(0.0,0.0); +double QGroundControlQmlGlobal::_zoom = 2; + QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app, QGCToolbox* toolbox) : QGCTool(app, toolbox) , _flightMapInitialZoom(17.0) @@ -41,11 +44,22 @@ QGroundControlQmlGlobal::QGroundControlQmlGlobal(QGCApplication* app, QGCToolbox { // We clear the parent on this object since we run into shutdown problems caused by hybrid qml app. Instead we let it leak on shutdown. setParent(NULL); + // Load last coordinates and zoom from config file + QSettings settings; + settings.beginGroup(_flightMapPositionSettingsGroup); + _coord.setLatitude(settings.value(_flightMapPositionLatitudeSettingsKey, _coord.latitude()).toDouble()); + _coord.setLongitude(settings.value(_flightMapPositionLongitudeSettingsKey, _coord.longitude()).toDouble()); + _zoom = settings.value(_flightMapZoomSettingsKey, _zoom).toDouble(); } QGroundControlQmlGlobal::~QGroundControlQmlGlobal() { - + // Save last coordinates and zoom to config file + QSettings settings; + settings.beginGroup(_flightMapPositionSettingsGroup); + settings.setValue(_flightMapPositionLatitudeSettingsKey, _coord.latitude()); + settings.setValue(_flightMapPositionLongitudeSettingsKey, _coord.longitude()); + settings.setValue(_flightMapZoomSettingsKey, _zoom); } void QGroundControlQmlGlobal::setToolbox(QGCToolbox* toolbox) @@ -198,34 +212,12 @@ void QGroundControlQmlGlobal::setSkipSetupPage(bool skip) } } -QGeoCoordinate QGroundControlQmlGlobal::flightMapPosition(void) -{ - QSettings settings; - QGeoCoordinate coord; - - settings.beginGroup(_flightMapPositionSettingsGroup); - coord.setLatitude(settings.value(_flightMapPositionLatitudeSettingsKey, 0).toDouble()); - coord.setLongitude(settings.value(_flightMapPositionLongitudeSettingsKey, 0).toDouble()); - - return coord; -} - -double QGroundControlQmlGlobal::flightMapZoom(void) -{ - QSettings settings; - - settings.beginGroup(_flightMapPositionSettingsGroup); - return settings.value(_flightMapZoomSettingsKey, 2).toDouble(); -} - void QGroundControlQmlGlobal::setFlightMapPosition(QGeoCoordinate& coordinate) { if (coordinate != flightMapPosition()) { - QSettings settings; + _coord.setLatitude(coordinate.latitude()); + _coord.setLongitude(coordinate.longitude()); - settings.beginGroup(_flightMapPositionSettingsGroup); - settings.setValue(_flightMapPositionLatitudeSettingsKey, coordinate.latitude()); - settings.setValue(_flightMapPositionLongitudeSettingsKey, coordinate.longitude()); emit flightMapPositionChanged(coordinate); } } @@ -233,10 +225,7 @@ void QGroundControlQmlGlobal::setFlightMapPosition(QGeoCoordinate& coordinate) void QGroundControlQmlGlobal::setFlightMapZoom(double zoom) { if (zoom != flightMapZoom()) { - QSettings settings; - - settings.beginGroup(_flightMapPositionSettingsGroup); - settings.setValue(_flightMapZoomSettingsKey, zoom); + _zoom = zoom; emit flightMapZoomChanged(zoom); } } diff --git a/src/QmlControls/QGroundControlQmlGlobal.h b/src/QmlControls/QGroundControlQmlGlobal.h index 4dfe9dcc867e083e633e9d0bd8f2336842c776a4..3f0fd2a5c28987ead2480fcf856ad313ac07de3a 100644 --- a/src/QmlControls/QGroundControlQmlGlobal.h +++ b/src/QmlControls/QGroundControlQmlGlobal.h @@ -142,8 +142,8 @@ public: QGCCorePlugin* corePlugin () { return _corePlugin; } SettingsManager* settingsManager () { return _settingsManager; } FactGroup* gpsRtkFactGroup () { return &_gpsRtkFactGroup; } - static QGeoCoordinate flightMapPosition (); - static double flightMapZoom (); + static QGeoCoordinate flightMapPosition () { return _coord; } + static double flightMapZoom () { return _zoom; } qreal zOrderTopMost () { return 1000; } qreal zOrderWidgets () { return 100; } @@ -209,6 +209,9 @@ private: static const char* _flightMapPositionLatitudeSettingsKey; static const char* _flightMapPositionLongitudeSettingsKey; static const char* _flightMapZoomSettingsKey; + + static QGeoCoordinate _coord; + static double _zoom; }; #endif diff --git a/src/QmlControls/QmlTest.qml b/src/QmlControls/QmlTest.qml index 92193f776be0b0f52361f5bcc2252a0b721dbbc6..830352630c7fb9eb0635863da2858a78faeed40c 100644 --- a/src/QmlControls/QmlTest.qml +++ b/src/QmlControls/QmlTest.qml @@ -138,7 +138,7 @@ Rectangle { // Header row Loader { sourceComponent: rowHeader - property var text: "" + property string text: "" } Text { width: 80 @@ -172,7 +172,7 @@ Rectangle { // window Loader { sourceComponent: rowHeader - property var text: "window" + property string text: "window" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -204,7 +204,7 @@ Rectangle { // windowShade Loader { sourceComponent: rowHeader - property var text: "windowShade" + property string text: "windowShade" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -236,7 +236,7 @@ Rectangle { // windowShadeDark Loader { sourceComponent: rowHeader - property var text: "windowShadeDark" + property string text: "windowShadeDark" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -268,7 +268,7 @@ Rectangle { // text Loader { sourceComponent: rowHeader - property var text: "text" + property string text: "text" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -300,7 +300,7 @@ Rectangle { // button Loader { sourceComponent: rowHeader - property var text: "button" + property string text: "button" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -332,7 +332,7 @@ Rectangle { // buttonText Loader { sourceComponent: rowHeader - property var text: "buttonText" + property string text: "buttonText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -364,7 +364,7 @@ Rectangle { // buttonHighlight Loader { sourceComponent: rowHeader - property var text: "buttonHighlight" + property string text: "buttonHighlight" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -396,7 +396,7 @@ Rectangle { // buttonHighlightText Loader { sourceComponent: rowHeader - property var text: "buttonHighlightText" + property string text: "buttonHighlightText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -428,7 +428,7 @@ Rectangle { // primaryButton Loader { sourceComponent: rowHeader - property var text: "primaryButton" + property string text: "primaryButton" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -460,7 +460,7 @@ Rectangle { // primaryButtonText Loader { sourceComponent: rowHeader - property var text: "primaryButtonText" + property string text: "primaryButtonText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -492,7 +492,7 @@ Rectangle { // textField Loader { sourceComponent: rowHeader - property var text: "textField" + property string text: "textField" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -524,7 +524,7 @@ Rectangle { // textFieldText Loader { sourceComponent: rowHeader - property var text: "textFieldText" + property string text: "textFieldText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -556,7 +556,7 @@ Rectangle { // warningText Loader { sourceComponent: rowHeader - property var text: "warningText" + property string text: "warningText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -588,7 +588,7 @@ Rectangle { // colorGreen Loader { sourceComponent: rowHeader - property var text: "colorGreen" + property string text: "colorGreen" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -620,7 +620,7 @@ Rectangle { // colorOrange Loader { sourceComponent: rowHeader - property var text: "colorOrange" + property string text: "colorOrange" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -652,7 +652,7 @@ Rectangle { // colorRed Loader { sourceComponent: rowHeader - property var text: "colorRed" + property string text: "colorRed" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -684,7 +684,7 @@ Rectangle { // colorGrey Loader { sourceComponent: rowHeader - property var text: "colorGrey" + property string text: "colorGrey" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -716,7 +716,7 @@ Rectangle { // colorBlue Loader { sourceComponent: rowHeader - property var text: "colorBlue" + property string text: "colorBlue" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -748,7 +748,7 @@ Rectangle { // alertBackground Loader { sourceComponent: rowHeader - property var text: "alertBackground" + property string text: "alertBackground" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -780,7 +780,7 @@ Rectangle { // alertBorder Loader { sourceComponent: rowHeader - property var text: "alertBorder" + property string text: "alertBorder" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -812,7 +812,7 @@ Rectangle { // alertText Loader { sourceComponent: rowHeader - property var text: "alertText" + property string text: "alertText" } ClickableColor { property var palette: QGCPalette { colorGroupEnabled: false } @@ -868,7 +868,7 @@ Rectangle { // Header row Loader { sourceComponent: ctlRowHeader - property var text: "" + property string text: "" } Text { width: 100 @@ -888,7 +888,7 @@ Rectangle { // QGCLabel Loader { sourceComponent: ctlRowHeader - property var text: "QGCLabel" + property string text: "QGCLabel" } QGCLabel { width: 100 @@ -905,7 +905,7 @@ Rectangle { // QGCButton Loader { sourceComponent: ctlRowHeader - property var text: "QGCButton" + property string text: "QGCButton" } QGCButton { width: 100 @@ -922,7 +922,7 @@ Rectangle { // QGCButton - primary Loader { sourceComponent: ctlRowHeader - property var text: "QGCButton(primary)" + property string text: "QGCButton(primary)" } QGCButton { width: 100 @@ -941,7 +941,7 @@ Rectangle { // QGCButton - menu Loader { sourceComponent: ctlRowHeader - property var text: "QGCButton(menu)" + property string text: "QGCButton(menu)" } Menu { id: buttonMenu @@ -972,7 +972,7 @@ Rectangle { // QGCRadioButton Loader { sourceComponent: ctlRowHeader - property var text: "QGCRadioButton" + property string text: "QGCRadioButton" } QGCRadioButton { width: 100 @@ -989,7 +989,7 @@ Rectangle { // QGCCheckBox Loader { sourceComponent: ctlRowHeader - property var text: "QGCCheckBox" + property string text: "QGCCheckBox" } QGCCheckBox { width: 100 @@ -1006,7 +1006,7 @@ Rectangle { // QGCTextField Loader { sourceComponent: ctlRowHeader - property var text: "QGCTextField" + property string text: "QGCTextField" } QGCTextField { width: 100 @@ -1023,7 +1023,7 @@ Rectangle { // QGCComboBox Loader { sourceComponent: ctlRowHeader - property var text: "QGCComboBox" + property string text: "QGCComboBox" } QGCComboBox { width: 100 @@ -1040,7 +1040,7 @@ Rectangle { // SubMenuButton Loader { sourceComponent: ctlRowHeader - property var text: "SubMenuButton" + property string text: "SubMenuButton" } SubMenuButton { width: 100 @@ -1079,15 +1079,15 @@ Rectangle { spacing: 10 anchors.horizontalCenter: parent.horizontalCenter Loader { - property var backgroundColor: qgcPal.window + property color backgroundColor: qgcPal.window sourceComponent: arbBox } Loader { - property var backgroundColor: qgcPal.windowShade + property color backgroundColor: qgcPal.windowShade sourceComponent: arbBox } Loader { - property var backgroundColor: qgcPal.windowShadeDark + property color backgroundColor: qgcPal.windowShadeDark sourceComponent: arbBox } } diff --git a/src/QmlControls/ScreenTools.qml b/src/QmlControls/ScreenTools.qml index d6df6c25f5e085dc8fde7b0dc956602f2a3472ce..900186e88c3cc95a8e3bd1665538197ceb698db7 100644 --- a/src/QmlControls/ScreenTools.qml +++ b/src/QmlControls/ScreenTools.qml @@ -60,6 +60,7 @@ Item { property bool isDebug: ScreenToolsController.isDebug property bool isTinyScreen: (Screen.width / Screen.pixelDensity) < 120 // 120mm property bool isShortScreen: ScreenToolsController.isMobile && ((Screen.height / Screen.width) < 0.6) // Nexus 7 for example + property bool isHugeScreen: Screen.width >= 1920*2 readonly property real minTouchMillimeters: 10 ///< Minimum touch size in millimeters property real minTouchPixels: 0 ///< Minimum touch size in pixels @@ -90,6 +91,10 @@ Item { } } + function printScreenStats() { + console.log('ScreenTools: Screen.width: ' + Screen.width + ' Screen.height: ' + Screen.height + ' Screen.pixelDensity: ' + Screen.pixelDensity) + } + /// Returns the current x position of the mouse in global screen coordinates. function mouseX() { return ScreenToolsController.mouseX() diff --git a/src/QmlControls/ToolStrip.qml b/src/QmlControls/ToolStrip.qml index 4d7d9aeebd548193390277908f58a205f734adb0..23cbfdda5708e1b5a37a9de1f19c5dd8a1e00c05 100644 --- a/src/QmlControls/ToolStrip.qml +++ b/src/QmlControls/ToolStrip.qml @@ -52,6 +52,10 @@ Rectangle { } } + DeadMouseArea { + anchors.fill: parent + } + Column { id: buttonStripColumn anchors.margins: ScreenTools.defaultFontPixelWidth / 2 diff --git a/src/QtLocationPlugin/QGCMapEngine.cpp b/src/QtLocationPlugin/QGCMapEngine.cpp index abcbcc413ceb0ee6277e2457bb32bd4223b26a80..fcd219fbd32954e31f46bd14b29aa9f1a7e92f4d 100644 --- a/src/QtLocationPlugin/QGCMapEngine.cpp +++ b/src/QtLocationPlugin/QGCMapEngine.cpp @@ -55,6 +55,7 @@ stQGeoTileCacheQGCMapTypes kMapTypes[] = { {"Bing Satellite Map", UrlFactory::BingSatellite}, {"Bing Hybrid Map", UrlFactory::BingHybrid}, {"Statkart Terrain Map", UrlFactory::StatkartTopo}, + {"ENIRO Terrain Map", UrlFactory::EniroTopo} /* {"MapQuest Street Map", UrlFactory::MapQuestMap}, {"MapQuest Satellite Map", UrlFactory::MapQuestSat} @@ -502,6 +503,7 @@ QGCMapEngine::concurrentDownloads(UrlFactory::MapType type) case UrlFactory::BingSatellite: case UrlFactory::BingHybrid: case UrlFactory::StatkartTopo: + case UrlFactory::EniroTopo: case UrlFactory::EsriWorldStreet: case UrlFactory::EsriWorldSatellite: case UrlFactory::EsriTerrain: diff --git a/src/QtLocationPlugin/QGCMapUrlEngine.cpp b/src/QtLocationPlugin/QGCMapUrlEngine.cpp index 74cea6cb0db96c3ff5a56d5aae34d429d006006c..0785e0a68c01fbc5152b3353639c54529fb3d1d3 100644 --- a/src/QtLocationPlugin/QGCMapUrlEngine.cpp +++ b/src/QtLocationPlugin/QGCMapUrlEngine.cpp @@ -98,6 +98,9 @@ UrlFactory::getImageFormat(MapType type, const QByteArray& image) case StatkartTopo: format = "png"; break; + case EniroTopo: + format = "png"; + break; /* case MapQuestMap: case MapQuestSat: @@ -163,6 +166,9 @@ UrlFactory::getTileURL(MapType type, int x, int y, int zoom, QNetworkAccessManag case StatkartTopo: request.setRawHeader("Referrer", "https://www.norgeskart.no/"); break; + case EniroTopo: + request.setRawHeader("Referrer", "https://www.eniro.se/"); + break; /* case OpenStreetMapSurfer: case OpenStreetMapSurferTerrain: @@ -269,6 +275,11 @@ UrlFactory::_getURL(MapType type, int x, int y, int zoom, QNetworkAccessManager* return QString("http://opencache.statkart.no/gatekeeper/gk/gk.open_gmaps?layers=topo2&zoom=%1&x=%2&y=%3").arg(zoom).arg(x).arg(y); } break; + case EniroTopo: + { + return QString("http://map.eniro.com/geowebcache/service/tms1.0.0/map/%1/%2/%3.png").arg(zoom).arg(x).arg((1<(mtask); + _deleteTileSet(task->setID()); + task->setTileSetDeleted(); +} + +//----------------------------------------------------------------------------- +void +QGCCacheWorker::_deleteTileSet(qulonglong id) +{ QSqlQuery query(*_db); QString s; //-- Only delete tiles unique to this set - s = QString("DELETE FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(task->setID()); + s = QString("DELETE FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(id); query.exec(s); - s = QString("DELETE FROM TilesDownload WHERE setID = %1").arg(task->setID()); + s = QString("DELETE FROM TilesDownload WHERE setID = %1").arg(id); query.exec(s); - s = QString("DELETE FROM TileSets WHERE setID = %1").arg(task->setID()); + s = QString("DELETE FROM TileSets WHERE setID = %1").arg(id); query.exec(s); - s = QString("DELETE FROM SetTiles WHERE setID = %1").arg(task->setID()); + s = QString("DELETE FROM SetTiles WHERE setID = %1").arg(id); query.exec(s); _updateTotals(); - task->setTileSetDeleted(); } //----------------------------------------------------------------------------- @@ -700,118 +707,146 @@ QGCCacheWorker::_importSets(QGCMapTask* mtask) //-- Prepare progress report quint64 tileCount = 0; quint64 currentCount = 0; + int lastProgress = -1; QString s; s = QString("SELECT COUNT(tileID) FROM Tiles"); if(query.exec(s)) { if(query.next()) { + //-- Total number of tiles in imported database tileCount = query.value(0).toULongLong(); } } - if(!tileCount) { - qWarning() << "No tiles found in imported database"; - tileCount = 1; //-- Let it run through - } - //-- Iterate Tile Sets - s = QString("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); - if(query.exec(s)) { - while(query.next()) { - QString name = query.value("name").toString(); - quint64 setID = query.value("setID").toULongLong(); - QString mapType = query.value("typeStr").toString(); - double topleftLat = query.value("topleftLat").toDouble(); - double topleftLon = query.value("topleftLon").toDouble(); - double bottomRightLat = query.value("bottomRightLat").toDouble(); - double bottomRightLon = query.value("bottomRightLon").toDouble(); - int minZoom = query.value("minZoom").toInt(); - int maxZoom = query.value("maxZoom").toInt(); - int type = query.value("type").toInt(); - quint32 numTiles = query.value("numTiles").toUInt(); - int defaultSet = query.value("defaultSet").toInt(); - quint64 insertSetID = _getDefaultTileSet(); - //-- If not default set, create new one - if(!defaultSet) { - //-- Check if we have this tile set already - int testCount = 0; - while (true) { - QString testName; - testName.sprintf("%s %03d", name.toLatin1().data(), ++testCount); - if(!_findTileSetID(testName, insertSetID) || testCount > 99) { - if(testCount > 1) { - name = testName; + if(tileCount) { + //-- Iterate Tile Sets + s = QString("SELECT * FROM TileSets ORDER BY defaultSet DESC, name ASC"); + if(query.exec(s)) { + while(query.next()) { + QString name = query.value("name").toString(); + quint64 setID = query.value("setID").toULongLong(); + QString mapType = query.value("typeStr").toString(); + double topleftLat = query.value("topleftLat").toDouble(); + double topleftLon = query.value("topleftLon").toDouble(); + double bottomRightLat = query.value("bottomRightLat").toDouble(); + double bottomRightLon = query.value("bottomRightLon").toDouble(); + int minZoom = query.value("minZoom").toInt(); + int maxZoom = query.value("maxZoom").toInt(); + int type = query.value("type").toInt(); + quint32 numTiles = query.value("numTiles").toUInt(); + int defaultSet = query.value("defaultSet").toInt(); + quint64 insertSetID = _getDefaultTileSet(); + //-- If not default set, create new one + if(!defaultSet) { + //-- Check if we have this tile set already + if(_findTileSetID(name, insertSetID)) { + int testCount = 0; + //-- Set with this name already exists. Make name unique. + while (true) { + QString testName; + testName.sprintf("%s %02d", name.toLatin1().data(), ++testCount); + if(!_findTileSetID(testName, insertSetID) || testCount > 99) { + name = testName; + break; + } } - break; } - } - //-- Create new set - QSqlQuery cQuery(*_db); - cQuery.prepare("INSERT INTO TileSets(" - "name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, defaultSet, date" - ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - cQuery.addBindValue(name); - cQuery.addBindValue(mapType); - cQuery.addBindValue(topleftLat); - cQuery.addBindValue(topleftLon); - cQuery.addBindValue(bottomRightLat); - cQuery.addBindValue(bottomRightLon); - cQuery.addBindValue(minZoom); - cQuery.addBindValue(maxZoom); - cQuery.addBindValue(type); - cQuery.addBindValue(numTiles); - cQuery.addBindValue(defaultSet); - cQuery.addBindValue(QDateTime::currentDateTime().toTime_t()); - if(!cQuery.exec()) { - task->setError("Error adding imported tile set to database"); - break; - } else { - //-- Get just created (auto-incremented) setID - insertSetID = cQuery.lastInsertId().toULongLong(); - } - } - //-- Find set tiles - QSqlQuery cQuery(*_db); - QSqlQuery subQuery(*dbImport); - QString sb = QString("SELECT * FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(setID); - if(subQuery.exec(sb)) { - _db->transaction(); - while(subQuery.next()) { - QString hash = subQuery.value("hash").toString(); - QString format = subQuery.value("format").toString(); - QByteArray img = subQuery.value("tile").toByteArray(); - int type = subQuery.value("type").toInt(); - //-- Save tile - cQuery.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); - cQuery.addBindValue(hash); - cQuery.addBindValue(format); - cQuery.addBindValue(img); - cQuery.addBindValue(img.size()); + //-- Create new set + QSqlQuery cQuery(*_db); + cQuery.prepare("INSERT INTO TileSets(" + "name, typeStr, topleftLat, topleftLon, bottomRightLat, bottomRightLon, minZoom, maxZoom, type, numTiles, defaultSet, date" + ") VALUES(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + cQuery.addBindValue(name); + cQuery.addBindValue(mapType); + cQuery.addBindValue(topleftLat); + cQuery.addBindValue(topleftLon); + cQuery.addBindValue(bottomRightLat); + cQuery.addBindValue(bottomRightLon); + cQuery.addBindValue(minZoom); + cQuery.addBindValue(maxZoom); cQuery.addBindValue(type); + cQuery.addBindValue(numTiles); + cQuery.addBindValue(defaultSet); cQuery.addBindValue(QDateTime::currentDateTime().toTime_t()); - if(cQuery.exec()) { - quint64 importTileID = cQuery.lastInsertId().toULongLong(); - QString s = QString("INSERT INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(importTileID).arg(insertSetID); - cQuery.prepare(s); - cQuery.exec(); - currentCount++; - task->setProgress((int)((double)currentCount / (double)tileCount * 100.0)); + if(!cQuery.exec()) { + task->setError("Error adding imported tile set to database"); + break; + } else { + //-- Get just created (auto-incremented) setID + insertSetID = cQuery.lastInsertId().toULongLong(); } } - _db->commit(); - //-- Update tile count - s = QString("SELECT COUNT(size) FROM Tiles A INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = %1").arg(insertSetID); - if(cQuery.exec(s)) { - if(cQuery.next()) { - quint64 count = cQuery.value(0).toULongLong(); - s = QString("UPDATE TileSets SET numTiles = %1 WHERE setID = %2").arg(count).arg(insertSetID); - cQuery.exec(s); + //-- Find set tiles + QSqlQuery cQuery(*_db); + QSqlQuery subQuery(*dbImport); + QString sb = QString("SELECT * FROM Tiles WHERE tileID IN (SELECT A.tileID FROM SetTiles A JOIN SetTiles B ON A.tileID = B.tileID WHERE B.setID = %1 GROUP BY A.tileID HAVING COUNT(A.tileID) = 1)").arg(setID); + if(subQuery.exec(sb)) { + quint64 tilesFound = 0; + quint64 tilesSaved = 0; + _db->transaction(); + while(subQuery.next()) { + tilesFound++; + QString hash = subQuery.value("hash").toString(); + QString format = subQuery.value("format").toString(); + QByteArray img = subQuery.value("tile").toByteArray(); + int type = subQuery.value("type").toInt(); + //-- Save tile + cQuery.prepare("INSERT INTO Tiles(hash, format, tile, size, type, date) VALUES(?, ?, ?, ?, ?, ?)"); + cQuery.addBindValue(hash); + cQuery.addBindValue(format); + cQuery.addBindValue(img); + cQuery.addBindValue(img.size()); + cQuery.addBindValue(type); + cQuery.addBindValue(QDateTime::currentDateTime().toTime_t()); + if(cQuery.exec()) { + tilesSaved++; + quint64 importTileID = cQuery.lastInsertId().toULongLong(); + QString s = QString("INSERT INTO SetTiles(tileID, setID) VALUES(%1, %2)").arg(importTileID).arg(insertSetID); + cQuery.prepare(s); + cQuery.exec(); + currentCount++; + if(tileCount) { + int progress = (int)((double)currentCount / (double)tileCount * 100.0); + //-- Avoid calling this if (int) progress hasn't changed. + if(lastProgress != progress) { + lastProgress = progress; + task->setProgress(progress); + } + } + } + } + _db->commit(); + if(tilesSaved) { + //-- Update tile count (if any added) + s = QString("SELECT COUNT(size) FROM Tiles A INNER JOIN SetTiles B on A.tileID = B.tileID WHERE B.setID = %1").arg(insertSetID); + if(cQuery.exec(s)) { + if(cQuery.next()) { + quint64 count = cQuery.value(0).toULongLong(); + s = QString("UPDATE TileSets SET numTiles = %1 WHERE setID = %2").arg(count).arg(insertSetID); + cQuery.exec(s); + } + } + } + qint64 uniqueTiles = tilesFound - tilesSaved; + if((quint64)uniqueTiles < tileCount) { + tileCount -= uniqueTiles; + } else { + tileCount = 0; + } + //-- If there was nothing new in this set, remove it. + if(!tilesSaved && !defaultSet) { + qCDebug(QGCTileCacheLog) << "No unique tiles in" << name << "Removing it."; + _deleteTileSet(insertSetID); } } } + } else { + task->setError("No tile set in database"); } - } else { - task->setError("No tile set in database"); } delete dbImport; QSqlDatabase::removeDatabase(kExportSession); + if(!tileCount) { + task->setError("No unique tiles in imported database"); + } } else { task->setError("Error opening import database"); } @@ -1071,6 +1106,9 @@ QGCCacheWorker::_lookupReady(QHostInfo info) _hostLookupID = 0; if(info.error() == QHostInfo::NoError && info.addresses().size()) { QTcpSocket socket; + QNetworkProxy tempProxy; + tempProxy.setType(QNetworkProxy::DefaultProxy); + socket.setProxy(tempProxy); socket.connectToHost(info.addresses().first(), 80); if (socket.waitForConnected(2000)) { qCDebug(QGCTileCacheLog) << "Yes Internet Access"; diff --git a/src/QtLocationPlugin/QGCTileCacheWorker.h b/src/QtLocationPlugin/QGCTileCacheWorker.h index a46a91117edc378c2cae604a8ae23b0833d6e759..e0ba1d4a04938305467a641d069b805e4a30f1fc 100644 --- a/src/QtLocationPlugin/QGCTileCacheWorker.h +++ b/src/QtLocationPlugin/QGCTileCacheWorker.h @@ -76,6 +76,7 @@ private: bool _createDB (QSqlDatabase *db, bool createDefault = true); quint64 _getDefaultTileSet (); void _updateTotals (); + void _deleteTileSet (qulonglong id); signals: void updateTotals (quint32 totaltiles, quint64 totalsize, quint32 defaulttiles, quint64 defaultsize); diff --git a/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp index f856dec931786c644b094a2a99b8806c6e21beb6..5a41dfeb56c6af590c72d836e567809262d58ded 100644 --- a/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp +++ b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.cpp @@ -50,11 +50,11 @@ #include #include -#if QT_VERSION < 0x050500 +#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) #include #else #include -#if QT_VERSION >= 0x050600 +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) #include #else #include @@ -64,7 +64,7 @@ #include #include -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) //----------------------------------------------------------------------------- QGeoTiledMapQGC::QGeoTiledMapQGC(QGeoTiledMappingManagerEngine *engine, QObject *parent) : QGeoTiledMap(engine, parent) @@ -73,12 +73,6 @@ QGeoTiledMapQGC::QGeoTiledMapQGC(QGeoTiledMappingManagerEngine *engine, QObject } #endif -#if QT_VERSION >= 0x050900 -#define QGCGEOMAPTYPE(a,b,c,d,e,f) QGeoMapType(a,b,c,d,e,f,QByteArray("QGroundControl")) -#else -#define QGCGEOMAPTYPE(a,b,c,d,e,f) QGeoMapType(a,b,c,d,e,f) -#endif - //----------------------------------------------------------------------------- QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString) : QGeoTiledMappingManagerEngine() @@ -92,6 +86,16 @@ QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVarian setTileSize(QSize(256, 256)); + // In Qt 5.10 QGeoMapType need QGeoCameraCapabilities as argument + // E.g: https://github.com/qt/qtlocation/blob/2b230b0a10d898979e9d5193f4da2e408b397fe3/src/plugins/geoservices/osm/qgeotiledmappingmanagerengineosm.cpp#L167 + #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) + #define QGCGEOMAPTYPE(a,b,c,d,e,f) QGeoMapType(a,b,c,d,e,f,QByteArray("QGroundControl"), cameraCaps) + #elif QT_VERSION >= QT_VERSION_CHECK(5, 9, 0) + #define QGCGEOMAPTYPE(a,b,c,d,e,f) QGeoMapType(a,b,c,d,e,f,QByteArray("QGroundControl")) + #else + #define QGCGEOMAPTYPE(a,b,c,d,e,f) QGeoMapType(a,b,c,d,e,f) + #endif + /* * Google and Bing don't seem kosher at all. This was based on original code from OpenPilot and heavily modified to be used in QGC. */ @@ -121,6 +125,8 @@ QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVarian // Statkart mapTypes << QGCGEOMAPTYPE(QGeoMapType::TerrainMap, "Statkart Terrain Map", "Statkart Terrain Map", false, false, UrlFactory::StatkartTopo); + // Eniro + mapTypes << QGCGEOMAPTYPE(QGeoMapType::TerrainMap, "Eniro Terrain Map", "Eniro Terrain Map", false, false, UrlFactory::EniroTopo); // Esri mapTypes << QGCGEOMAPTYPE(QGeoMapType::StreetMap, "Esri Street Map", "ArcGIS Online World Street Map", true, false, UrlFactory::EsriWorldStreet); @@ -163,7 +169,7 @@ QGeoTiledMappingManagerEngineQGC::QGeoTiledMappingManagerEngineQGC(const QVarian getQGCMapEngine()->setUserAgent(parameters.value(QStringLiteral("useragent")).toString().toLatin1()); } -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) _setCache(parameters); #endif @@ -178,7 +184,7 @@ QGeoTiledMappingManagerEngineQGC::~QGeoTiledMappingManagerEngineQGC() { } -#if QT_VERSION < 0x050500 +#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) //----------------------------------------------------------------------------- QGeoMapData *QGeoTiledMappingManagerEngineQGC::createMapData() @@ -197,7 +203,7 @@ QGeoTiledMappingManagerEngineQGC::createMap() #endif -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) //----------------------------------------------------------------------------- void QGeoTiledMappingManagerEngineQGC::_setCache(const QVariantMap ¶meters) @@ -240,7 +246,7 @@ QGeoTiledMappingManagerEngineQGC::_setCache(const QVariantMap ¶meters) if(memLimit > 1024 * 1024 * 1024) memLimit = 1024 * 1024 * 1024; //-- Disable Qt's disk cache (sort of) -#if QT_VERSION >= 0x050600 +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) QAbstractGeoTileCache *pTileCache = new QGeoFileTileCache(cacheDir); setTileCache(pTileCache); #else diff --git a/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h index f3fe98aaba5487982202be636adefb231255a0ad..b434b7c6597ac6276c126109588d8e31609582bf 100644 --- a/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h +++ b/src/QtLocationPlugin/QGeoTiledMappingManagerEngineQGC.h @@ -48,12 +48,12 @@ #define QGEOTILEDMAPPINGMANAGERENGINEQGC_H #include -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) #include #endif #include -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) class QGeoTiledMapQGC : public QGeoTiledMap { Q_OBJECT @@ -70,13 +70,13 @@ class QGeoTiledMappingManagerEngineQGC : public QGeoTiledMappingManagerEngine public: QGeoTiledMappingManagerEngineQGC(const QVariantMap ¶meters, QGeoServiceProvider::Error *error, QString *errorString); ~QGeoTiledMappingManagerEngineQGC(); -#if QT_VERSION < 0x050500 +#if QT_VERSION < QT_VERSION_CHECK(5, 5, 0) QGeoMapData *createMapData(); #else QGeoMap *createMap(); #endif private: -#if QT_VERSION >= 0x050500 +#if QT_VERSION >= QT_VERSION_CHECK(5, 5, 0) void _setCache(const QVariantMap ¶meters); #endif }; diff --git a/src/QtLocationPlugin/QMLControl/OfflineMap.qml b/src/QtLocationPlugin/QMLControl/OfflineMap.qml index 9fc876e55db96495ab014334793e388248c93655..28875238028ce1c63b799d65c1b005174884101c 100644 --- a/src/QtLocationPlugin/QMLControl/OfflineMap.qml +++ b/src/QtLocationPlugin/QMLControl/OfflineMap.qml @@ -654,11 +654,9 @@ QGCView { color: Qt.rgba(qgcPal.window.r, qgcPal.window.g, qgcPal.window.b, 0.85) radius: ScreenTools.defaultFontPixelWidth * 0.5 - MouseArea { - anchors.fill: parent - onWheel: { wheel.accepted = true; } - onPressed: { mouse.accepted = true; } - onReleased: { mouse.accepted = true; } + //-- Eat mouse events + DeadMouseArea { + anchors.fill: parent } QGCLabel { @@ -928,11 +926,14 @@ QGCView { width: Math.min(_tileSetList.width, (ScreenTools.defaultFontPixelWidth * 50).toFixed(0)) spacing: ScreenTools.defaultFontPixelHeight * 0.5 anchors.horizontalCenter: parent.horizontalCenter + ExclusiveGroup { id: selectionGroup } OfflineMapButton { id: firstButton text: qsTr("Add New Set") width: _cacheList.width - height: ScreenTools.defaultFontPixelHeight * 2 + height: ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 3 : 2) + currentSet: _currentSelection + exclusiveGroup: selectionGroup onClicked: { offlineMapView._currentSelection = null addNewSet() @@ -946,7 +947,10 @@ QGCView { tiles: object.totalTileCount complete: object.complete width: firstButton.width - height: ScreenTools.defaultFontPixelHeight * 2 + height: ScreenTools.defaultFontPixelHeight * (ScreenTools.isMobile ? 3 : 2) + exclusiveGroup: selectionGroup + currentSet: _currentSelection + tileSet: object onClicked: { offlineMapView._currentSelection = object showInfo() diff --git a/src/Settings/App.SettingsGroup.json b/src/Settings/App.SettingsGroup.json index 5b9bd15c2219da680b21cb0f958944e8f6670df7..81520a00d83d844c8af5b5436cfe0616471935de 100644 --- a/src/Settings/App.SettingsGroup.json +++ b/src/Settings/App.SettingsGroup.json @@ -103,6 +103,13 @@ "type": "bool", "defaultValue": false }, +{ + "name": "GstreamerDebugLevel", + "shortDescription": "Video streaming debug", + "longDescription": "Sets the environment variable GST_DEBUG for all pipeline elements on boot.", + "type": "uint8", + "defaultValue": 0 +}, { "name": "AutoLoadMissions", "shortDescription": "AutoLoad mission on vehicle connect", diff --git a/src/Settings/AppSettings.cc b/src/Settings/AppSettings.cc index 65068ef39ef40bd38d44838f2f86cffec647a62b..3e8b950e88401411b79425f9ede36e74db97f3eb 100644 --- a/src/Settings/AppSettings.cc +++ b/src/Settings/AppSettings.cc @@ -36,6 +36,7 @@ const char* AppSettings::autoLoadMissionsName = "AutoLoa const char* AppSettings::mapboxTokenName = "MapboxToken"; const char* AppSettings::esriTokenName = "EsriToken"; const char* AppSettings::defaultFirmwareTypeName = "DefaultFirmwareType"; +const char* AppSettings::gstDebugName = "GstreamerDebugLevel"; const char* AppSettings::parameterFileExtension = "params"; const char* AppSettings::planFileExtension = "plan"; @@ -75,6 +76,7 @@ AppSettings::AppSettings(QObject* parent) , _mapboxTokenFact(NULL) , _esriTokenFact(NULL) , _defaultFirmwareTypeFact(NULL) + , _gstDebugFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "AppSettings", "Reference only"); @@ -232,6 +234,15 @@ Fact* AppSettings::virtualJoystick(void) return _virtualJoystickFact; } +Fact* AppSettings::gstDebug(void) +{ + if (!_gstDebugFact) { + _gstDebugFact = _createSettingsFact(gstDebugName); + } + + return _gstDebugFact; +} + Fact* AppSettings::indoorPalette(void) { if (!_indoorPaletteFact) { diff --git a/src/Settings/AppSettings.h b/src/Settings/AppSettings.h index 5b29ac37096b61e46ef5ca16e5b1b593edbf89e5..6b421aa35ddac5ce78e53ba0a85d3920af3992c7 100644 --- a/src/Settings/AppSettings.h +++ b/src/Settings/AppSettings.h @@ -40,6 +40,7 @@ public: Q_PROPERTY(Fact* mapboxToken READ mapboxToken CONSTANT) Q_PROPERTY(Fact* esriToken READ esriToken CONSTANT) Q_PROPERTY(Fact* defaultFirmwareType READ defaultFirmwareType CONSTANT) + Q_PROPERTY(Fact* gstDebug READ gstDebug CONSTANT) Q_PROPERTY(QString missionSavePath READ missionSavePath NOTIFY savePathsChanged) Q_PROPERTY(QString parameterSavePath READ parameterSavePath NOTIFY savePathsChanged) @@ -75,6 +76,7 @@ public: Fact* mapboxToken (void); Fact* esriToken (void); Fact* defaultFirmwareType (void); + Fact* gstDebug (void); QString missionSavePath (void); QString parameterSavePath (void); @@ -107,6 +109,7 @@ public: static const char* mapboxTokenName; static const char* esriTokenName; static const char* defaultFirmwareTypeName; + static const char* gstDebugName; // Application wide file extensions static const char* parameterFileExtension; @@ -154,6 +157,7 @@ private: SettingsFact* _mapboxTokenFact; SettingsFact* _esriTokenFact; SettingsFact* _defaultFirmwareTypeFact; + SettingsFact* _gstDebugFact; }; #endif diff --git a/src/Settings/AutoConnect.SettingsGroup.json b/src/Settings/AutoConnect.SettingsGroup.json index 5abfc0dd5739375ad126de4f455a7e48c956db4e..4a317fa4edde11516659641a053c5d8dc4f731b0 100644 --- a/src/Settings/AutoConnect.SettingsGroup.json +++ b/src/Settings/AutoConnect.SettingsGroup.json @@ -41,6 +41,20 @@ "type": "bool", "defaultValue": true }, +{ + "name": "AutoconnectNmeaPort", + "shortDescription": "NMEA GPS device for GCS position", + "longDescription": "NMEA GPS device for GCS position", + "type": "string", + "defaultValue": "disabled" +}, +{ + "name": "AutoconnectNmeaBaud", + "shortDescription": "NMEA GPS Baudrate", + "longDescription": "NMEA GPS Baudrate", + "type": "uint32", + "defaultValue": 4800 +}, { "name": "AutoconnectUDPListenPort", "shortDescription": "UDP port for autoconnect", diff --git a/src/Settings/AutoConnectSettings.cc b/src/Settings/AutoConnectSettings.cc index ab6bf4e5743872c5aa79f22afa4dcc600095d319..cb5a403b2ff340e5c0ffdf582536f76be1f892c9 100644 --- a/src/Settings/AutoConnectSettings.cc +++ b/src/Settings/AutoConnectSettings.cc @@ -21,6 +21,8 @@ const char* AutoConnectSettings:: autoConnectSiKRadioSettingsName = "Autocon const char* AutoConnectSettings:: autoConnectPX4FlowSettingsName = "AutoconnectPX4Flow"; const char* AutoConnectSettings:: autoConnectRTKGPSSettingsName = "AutoconnectRTKGPS"; const char* AutoConnectSettings:: autoConnectLibrePilotSettingsName = "AutoconnectLibrePilot"; +const char* AutoConnectSettings:: autoConnectNmeaPortName = "AutoconnectNmeaPort"; +const char* AutoConnectSettings:: autoConnectNmeaBaudName = "AutoconnectNmeaBaud"; const char* AutoConnectSettings:: udpListenPortName = "AutoconnectUDPListenPort"; const char* AutoConnectSettings:: udpTargetHostIPName = "AutoconnectUDPTargetHostIP"; const char* AutoConnectSettings:: udpTargetHostPortName = "AutoconnectUDPTargetHostPort"; @@ -35,6 +37,8 @@ AutoConnectSettings::AutoConnectSettings(QObject* parent) , _autoConnectPX4FlowFact (NULL) , _autoConnectRTKGPSFact (NULL) , _autoConnectLibrePilotFact(NULL) + , _autoConnectNmeaPortFact (NULL) + , _autoConnectNmeaBaudFact (NULL) , _udpListenPortFact (NULL) , _udpTargetHostIPFact (NULL) , _udpTargetHostPortFact (NULL) @@ -56,6 +60,9 @@ Fact* AutoConnectSettings::autoConnectPixhawk(void) { if (!_autoConnectPixhawkFact) { _autoConnectPixhawkFact = _createSettingsFact(autoConnectPixhawkSettingsName); +#ifdef __ios__ + _autoConnectPixhawkFact->setVisible(false); +#endif } return _autoConnectPixhawkFact; @@ -65,6 +72,9 @@ Fact* AutoConnectSettings::autoConnectSiKRadio(void) { if (!_autoConnectSiKRadioFact) { _autoConnectSiKRadioFact = _createSettingsFact(autoConnectSiKRadioSettingsName); +#ifdef __ios__ + _autoConnectSiKRadioFact->setVisible(false); +#endif } return _autoConnectSiKRadioFact; @@ -74,6 +84,9 @@ Fact* AutoConnectSettings::autoConnectPX4Flow(void) { if (!_autoConnectPX4FlowFact) { _autoConnectPX4FlowFact = _createSettingsFact(autoConnectPX4FlowSettingsName); +#ifdef __ios__ + _autoConnectPX4FlowFact->setVisible(false); +#endif } return _autoConnectPX4FlowFact; @@ -83,6 +96,9 @@ Fact* AutoConnectSettings::autoConnectRTKGPS(void) { if (!_autoConnectRTKGPSFact) { _autoConnectRTKGPSFact = _createSettingsFact(autoConnectRTKGPSSettingsName); +#ifdef __ios__ + _autoConnectRTKGPSFact->setVisible(false); +#endif } return _autoConnectRTKGPSFact; @@ -92,11 +108,38 @@ Fact* AutoConnectSettings::autoConnectLibrePilot(void) { if (!_autoConnectLibrePilotFact) { _autoConnectLibrePilotFact = _createSettingsFact(autoConnectLibrePilotSettingsName); +#ifdef __ios__ + _autoConnectLibrePilotFact->setVisible(false); +#endif } return _autoConnectLibrePilotFact; } +Fact* AutoConnectSettings::autoConnectNmeaPort(void) +{ + if (!_autoConnectNmeaPortFact) { + _autoConnectNmeaPortFact = _createSettingsFact(autoConnectNmeaPortName); +#ifdef __ios__ + _autoConnectNmeaPortFact->setVisible(false); +#endif + } + + return _autoConnectNmeaPortFact; +} + +Fact* AutoConnectSettings::autoConnectNmeaBaud(void) +{ + if (!_autoConnectNmeaBaudFact) { + _autoConnectNmeaBaudFact = _createSettingsFact(autoConnectNmeaBaudName); +#ifdef __ios__ + _autoConnectNmeaBaudFact->setVisible(false); +#endif + } + + return _autoConnectNmeaBaudFact; +} + Fact* AutoConnectSettings::udpListenPort(void) { if (!_udpListenPortFact) { diff --git a/src/Settings/AutoConnectSettings.h b/src/Settings/AutoConnectSettings.h index 405cc8f23e58e8005b419ef4c8c1c7c84409382a..74081bd76e9ab8107d91972f31b3afeb7f33c2c5 100644 --- a/src/Settings/AutoConnectSettings.h +++ b/src/Settings/AutoConnectSettings.h @@ -25,6 +25,8 @@ public: Q_PROPERTY(Fact* autoConnectPX4Flow READ autoConnectPX4Flow CONSTANT) Q_PROPERTY(Fact* autoConnectRTKGPS READ autoConnectRTKGPS CONSTANT) Q_PROPERTY(Fact* autoConnectLibrePilot READ autoConnectLibrePilot CONSTANT) + Q_PROPERTY(Fact* autoConnectNmeaPort READ autoConnectNmeaPort CONSTANT) + Q_PROPERTY(Fact* autoConnectNmeaBaud READ autoConnectNmeaBaud CONSTANT) Q_PROPERTY(Fact* udpListenPort READ udpListenPort CONSTANT) ///< Port to listen on for UDP autoconnect Q_PROPERTY(Fact* udpTargetHostIP READ udpTargetHostIP CONSTANT) ///< Target host IP for UDP autoconnect, empty string for none Q_PROPERTY(Fact* udpTargetHostPort READ udpTargetHostPort CONSTANT) ///< Target host post for UDP autoconnect @@ -35,6 +37,8 @@ public: Fact* autoConnectPX4Flow (void); Fact* autoConnectRTKGPS (void); Fact* autoConnectLibrePilot (void); + Fact* autoConnectNmeaPort (void); + Fact* autoConnectNmeaBaud (void); Fact* udpListenPort (void); Fact* udpTargetHostIP (void); Fact* udpTargetHostPort (void); @@ -47,6 +51,8 @@ public: static const char* autoConnectPX4FlowSettingsName; static const char* autoConnectRTKGPSSettingsName; static const char* autoConnectLibrePilotSettingsName; + static const char* autoConnectNmeaPortName; + static const char* autoConnectNmeaBaudName; static const char* udpListenPortName; static const char* udpTargetHostIPName; static const char* udpTargetHostPortName; @@ -58,6 +64,8 @@ private: SettingsFact* _autoConnectPX4FlowFact; SettingsFact* _autoConnectRTKGPSFact; SettingsFact* _autoConnectLibrePilotFact; + SettingsFact* _autoConnectNmeaPortFact; + SettingsFact* _autoConnectNmeaBaudFact; SettingsFact* _udpListenPortFact; SettingsFact* _udpTargetHostIPFact; SettingsFact* _udpTargetHostPortFact; diff --git a/src/Settings/FlightMap.SettingsGroup.json b/src/Settings/FlightMap.SettingsGroup.json index c3e30e576aeb81697f781ce8a76c308e2c35d793..a931205e455b1101fbc30382ac5308eb27e8fe06 100644 --- a/src/Settings/FlightMap.SettingsGroup.json +++ b/src/Settings/FlightMap.SettingsGroup.json @@ -3,8 +3,8 @@ "name": "MapProvider", "shortDescription": "Currently selected map provider for flight maps", "type": "uint32", - "enumStrings": "Bing,Google,Statkart,Mapbox,Esri", - "enumValues": "0,1,2,3,4", + "enumStrings": "Bing,Google,Statkart,Mapbox,Esri,Eniro", + "enumValues": "0,1,2,3,4,5", "defaultValue": 0 }, { diff --git a/src/Settings/FlightMapSettings.cc b/src/Settings/FlightMapSettings.cc index c814f856aac063bbe226603d8eb7aa1024d3b23b..7f501614a2029cca46d829aa17880c6f78f8ab94 100644 --- a/src/Settings/FlightMapSettings.cc +++ b/src/Settings/FlightMapSettings.cc @@ -110,6 +110,11 @@ void FlightMapSettings::_newMapProvider(QVariant value) _removeEnumValue(mapTypeSatellite, enumStrings, enumValues); _removeEnumValue(mapTypeHybrid, enumStrings, enumValues); break; + case mapProviderEniro: + _removeEnumValue(mapTypeStreet, enumStrings, enumValues); + _removeEnumValue(mapTypeSatellite, enumStrings, enumValues); + _removeEnumValue(mapTypeHybrid, enumStrings, enumValues); + break; case mapProviderEsri: _removeEnumValue(mapTypeHybrid, enumStrings, enumValues); break; diff --git a/src/Settings/FlightMapSettings.h b/src/Settings/FlightMapSettings.h index e849bfe6e6e65d41a91b892f25d1a6e379ba4483..d7dc2a373a207479d408e090f834bece491124ef 100644 --- a/src/Settings/FlightMapSettings.h +++ b/src/Settings/FlightMapSettings.h @@ -25,7 +25,8 @@ public: mapProviderGoogle, mapProviderStarkart, mapProviderMapbox, - mapProviderEsri + mapProviderEsri, + mapProviderEniro } MapProvider_t; // This enum must match the json meta data diff --git a/src/Settings/UnitsSettings.cc b/src/Settings/UnitsSettings.cc index 0d5c6e0d6df29fd8ecaf43ea5839bb286874866b..cd5988891cbfadc336882b45e70f667bf769a0c9 100644 --- a/src/Settings/UnitsSettings.cc +++ b/src/Settings/UnitsSettings.cc @@ -16,12 +16,14 @@ const char* UnitsSettings::unitsSettingsGroupName = "Units"; const char* UnitsSettings::distanceUnitsSettingsName = "DistanceUnits"; const char* UnitsSettings::areaUnitsSettingsName = "AreaUnits"; const char* UnitsSettings::speedUnitsSettingsName = "SpeedUnits"; +const char* UnitsSettings::temperatureUnitsSettingsName = "TemperatureUnits"; UnitsSettings::UnitsSettings(QObject* parent) : SettingsGroup(unitsSettingsGroupName, QString() /* root settings group */, parent) , _distanceUnitsFact(NULL) , _areaUnitsFact(NULL) , _speedUnitsFact(NULL) + , _temperatureUnitsFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "UnitsSettings", "Reference only"); @@ -92,3 +94,24 @@ Fact* UnitsSettings::speedUnits(void) return _speedUnitsFact; } + +Fact* UnitsSettings::temperatureUnits(void) +{ + if (!_temperatureUnitsFact) { + // Units settings can't be loaded from json since it creates an infinite loop of meta data loading. + QStringList enumStrings; + QVariantList enumValues; + + enumStrings << "Celsius" << "Farenheit"; + enumValues << QVariant::fromValue((uint32_t)TemperatureUnitsCelsius) << QVariant::fromValue((uint32_t)TemperatureUnitsFarenheit); + + FactMetaData* metaData = new FactMetaData(FactMetaData::valueTypeUint32, this); + metaData->setName(temperatureUnitsSettingsName); + metaData->setEnumInfo(enumStrings, enumValues); + metaData->setRawDefaultValue(TemperatureUnitsCelsius); + + _temperatureUnitsFact = new SettingsFact(QString() /* no settings group */, metaData, this); + } + + return _temperatureUnitsFact; +} diff --git a/src/Settings/UnitsSettings.h b/src/Settings/UnitsSettings.h index 3ede8765a39cd7bd0bdd929ae9a7aec3d377c953..1d09201c5cc49533689ddb620e77b1c352a58300 100644 --- a/src/Settings/UnitsSettings.h +++ b/src/Settings/UnitsSettings.h @@ -41,28 +41,38 @@ public: SpeedUnitsKnots, }; + enum TemperatureUnits { + TemperatureUnitsCelsius = 0, + TemperatureUnitsFarenheit, + }; + Q_ENUMS(DistanceUnits) Q_ENUMS(AreaUnits) Q_ENUMS(SpeedUnits) + Q_ENUMS(TemperatureUnits) Q_PROPERTY(Fact* distanceUnits READ distanceUnits CONSTANT) Q_PROPERTY(Fact* areaUnits READ areaUnits CONSTANT) Q_PROPERTY(Fact* speedUnits READ speedUnits CONSTANT) + Q_PROPERTY(Fact* temperatureUnits READ temperatureUnits CONSTANT) Fact* distanceUnits (void); Fact* areaUnits (void); Fact* speedUnits (void); + Fact* temperatureUnits (void); static const char* unitsSettingsGroupName; static const char* distanceUnitsSettingsName; static const char* areaUnitsSettingsName; static const char* speedUnitsSettingsName; + static const char* temperatureUnitsSettingsName; private: SettingsFact* _distanceUnitsFact; SettingsFact* _areaUnitsFact; SettingsFact* _speedUnitsFact; + SettingsFact* _temperatureUnitsFact; }; #endif diff --git a/src/Settings/Video.SettingsGroup.json b/src/Settings/Video.SettingsGroup.json index 68088d4e68c7fe160f5beed4678f8b7ed10cf0bb..20ee46b381866722d25f8381900954497fad390c 100644 --- a/src/Settings/Video.SettingsGroup.json +++ b/src/Settings/Video.SettingsGroup.json @@ -94,5 +94,19 @@ "min": 1, "units": "s", "defaultValue": 2 +}, +{ + "name": "StreamEnabled", + "shortDescription": "Video Stream Enabled", + "longDescription": "Start/Stop Video Stream.", + "type": "bool", + "defaultValue": true +}, +{ + "name": "DisableWhenDisarmed", + "shortDescription": "Video Stream Disnabled When Armed", + "longDescription": "Disable Video Stream when disarmed.", + "type": "bool", + "defaultValue": false } ] diff --git a/src/Settings/VideoSettings.cc b/src/Settings/VideoSettings.cc index 631a7d14a202390924513fe4984a4e38a6df3759..cb6ab514f1eeca4f5250f42d4d82017caf65d53e 100644 --- a/src/Settings/VideoSettings.cc +++ b/src/Settings/VideoSettings.cc @@ -30,6 +30,8 @@ const char* VideoSettings::recordingFormatName = "RecordingFormat"; const char* VideoSettings::maxVideoSizeName = "MaxVideoSize"; const char* VideoSettings::enableStorageLimitName = "EnableStorageLimit"; const char* VideoSettings::rtspTimeoutName = "RtspTimeout"; +const char* VideoSettings::streamEnabledName = "StreamEnabled"; +const char* VideoSettings::disableWhenDisarmedName ="DisableWhenDisarmed"; const char* VideoSettings::videoSourceNoVideo = "No Video Available"; const char* VideoSettings::videoDisabled = "Video Stream Disabled"; @@ -50,6 +52,8 @@ VideoSettings::VideoSettings(QObject* parent) , _maxVideoSizeFact(NULL) , _enableStorageLimitFact(NULL) , _rtspTimeoutFact(NULL) + , _streamEnabledFact(NULL) + , _disableWhenDisarmedFact(NULL) { QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); qmlRegisterUncreatableType("QGroundControl.SettingsManager", 1, 0, "VideoSettings", "Reference only"); @@ -94,8 +98,8 @@ Fact* VideoSettings::videoSource(void) { if (!_videoSourceFact) { _videoSourceFact = _createSettingsFact(videoSourceName); + connect(_videoSourceFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _videoSourceFact; } @@ -103,8 +107,8 @@ Fact* VideoSettings::udpPort(void) { if (!_udpPortFact) { _udpPortFact = _createSettingsFact(udpPortName); + connect(_udpPortFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _udpPortFact; } @@ -112,8 +116,8 @@ Fact* VideoSettings::rtspUrl(void) { if (!_rtspUrlFact) { _rtspUrlFact = _createSettingsFact(rtspUrlName); + connect(_rtspUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _rtspUrlFact; } @@ -121,8 +125,8 @@ Fact* VideoSettings::tcpUrl(void) { if (!_tcpUrlFact) { _tcpUrlFact = _createSettingsFact(tcpUrlName); + connect(_tcpUrlFact, &Fact::valueChanged, this, &VideoSettings::_configChanged); } - return _tcpUrlFact; } @@ -131,7 +135,6 @@ Fact* VideoSettings::aspectRatio(void) if (!_videoAspectRatioFact) { _videoAspectRatioFact = _createSettingsFact(videoAspectRatioName); } - return _videoAspectRatioFact; } @@ -140,7 +143,6 @@ Fact* VideoSettings::gridLines(void) if (!_gridLinesFact) { _gridLinesFact = _createSettingsFact(videoGridLinesName); } - return _gridLinesFact; } @@ -149,7 +151,6 @@ Fact* VideoSettings::showRecControl(void) if (!_showRecControlFact) { _showRecControlFact = _createSettingsFact(showRecControlName); } - return _showRecControlFact; } @@ -158,7 +159,6 @@ Fact* VideoSettings::recordingFormat(void) if (!_recordingFormatFact) { _recordingFormatFact = _createSettingsFact(recordingFormatName); } - return _recordingFormatFact; } @@ -167,7 +167,6 @@ Fact* VideoSettings::maxVideoSize(void) if (!_maxVideoSizeFact) { _maxVideoSizeFact = _createSettingsFact(maxVideoSizeName); } - return _maxVideoSizeFact; } @@ -176,7 +175,6 @@ Fact* VideoSettings::enableStorageLimit(void) if (!_enableStorageLimitFact) { _enableStorageLimitFact = _createSettingsFact(enableStorageLimitName); } - return _enableStorageLimitFact; } @@ -185,6 +183,50 @@ Fact* VideoSettings::rtspTimeout(void) if (!_rtspTimeoutFact) { _rtspTimeoutFact = _createSettingsFact(rtspTimeoutName); } - return _rtspTimeoutFact; } + +Fact* VideoSettings::streamEnabled(void) +{ + if (!_streamEnabledFact) { + _streamEnabledFact = _createSettingsFact(streamEnabledName); + } + return _streamEnabledFact; +} + +Fact* VideoSettings::disableWhenDisarmed(void) +{ + if (!_disableWhenDisarmedFact) { + _disableWhenDisarmedFact = _createSettingsFact(disableWhenDisarmedName); + } + return _disableWhenDisarmedFact; +} + +bool VideoSettings::streamConfigured(void) +{ +#if !defined(QGC_GST_STREAMING) + return false; +#endif + QString vSource = videoSource()->rawValue().toString(); + if(vSource == videoSourceNoVideo || vSource == videoDisabled) { + return false; + } + //-- If UDP, check if port is set + if(vSource == videoSourceUDP) { + return udpPort()->rawValue().toInt() != 0; + } + //-- If RTSP, check for URL + if(vSource == videoSourceRTSP) { + return !rtspUrl()->rawValue().toString().isEmpty(); + } + //-- If TCP, check for URL + if(vSource == videoSourceTCP) { + return !tcpUrl()->rawValue().toString().isEmpty(); + } + return false; +} + +void VideoSettings::_configChanged(QVariant) +{ + emit streamConfiguredChanged(); +} diff --git a/src/Settings/VideoSettings.h b/src/Settings/VideoSettings.h index e05816a1cc561cbd4b05c029cf8b3c1e36ddb062..63b6840d165d5d110b478f0e34608c53f70dec5b 100644 --- a/src/Settings/VideoSettings.h +++ b/src/Settings/VideoSettings.h @@ -19,29 +19,35 @@ class VideoSettings : public SettingsGroup public: VideoSettings(QObject* parent = NULL); - Q_PROPERTY(Fact* videoSource READ videoSource CONSTANT) - Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) - Q_PROPERTY(Fact* tcpUrl READ tcpUrl CONSTANT) - Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) - Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) - Q_PROPERTY(Fact* gridLines READ gridLines CONSTANT) - Q_PROPERTY(Fact* showRecControl READ showRecControl CONSTANT) - Q_PROPERTY(Fact* recordingFormat READ recordingFormat CONSTANT) - Q_PROPERTY(Fact* maxVideoSize READ maxVideoSize CONSTANT) - Q_PROPERTY(Fact* enableStorageLimit READ enableStorageLimit CONSTANT) - Q_PROPERTY(Fact* rtspTimeout READ rtspTimeout CONSTANT) + Q_PROPERTY(Fact* videoSource READ videoSource CONSTANT) + Q_PROPERTY(Fact* udpPort READ udpPort CONSTANT) + Q_PROPERTY(Fact* tcpUrl READ tcpUrl CONSTANT) + Q_PROPERTY(Fact* rtspUrl READ rtspUrl CONSTANT) + Q_PROPERTY(Fact* aspectRatio READ aspectRatio CONSTANT) + Q_PROPERTY(Fact* gridLines READ gridLines CONSTANT) + Q_PROPERTY(Fact* showRecControl READ showRecControl CONSTANT) + Q_PROPERTY(Fact* recordingFormat READ recordingFormat CONSTANT) + Q_PROPERTY(Fact* maxVideoSize READ maxVideoSize CONSTANT) + Q_PROPERTY(Fact* enableStorageLimit READ enableStorageLimit CONSTANT) + Q_PROPERTY(Fact* rtspTimeout READ rtspTimeout CONSTANT) + Q_PROPERTY(Fact* streamEnabled READ streamEnabled CONSTANT) + Q_PROPERTY(Fact* disableWhenDisarmed READ disableWhenDisarmed CONSTANT) + Q_PROPERTY(bool streamConfigured READ streamConfigured NOTIFY streamConfiguredChanged) - Fact* videoSource (void); - Fact* udpPort (void); - Fact* rtspUrl (void); - Fact* tcpUrl (void); - Fact* aspectRatio (void); - Fact* gridLines (void); - Fact* showRecControl (void); - Fact* recordingFormat (void); - Fact* maxVideoSize (void); - Fact* enableStorageLimit(void); - Fact* rtspTimeout (void); + Fact* videoSource (void); + Fact* udpPort (void); + Fact* rtspUrl (void); + Fact* tcpUrl (void); + Fact* aspectRatio (void); + Fact* gridLines (void); + Fact* showRecControl (void); + Fact* recordingFormat (void); + Fact* maxVideoSize (void); + Fact* enableStorageLimit (void); + Fact* rtspTimeout (void); + Fact* streamEnabled (void); + Fact* disableWhenDisarmed (void); + bool streamConfigured (void); static const char* videoSettingsGroupName; @@ -56,6 +62,8 @@ public: static const char* maxVideoSizeName; static const char* enableStorageLimitName; static const char* rtspTimeoutName; + static const char* streamEnabledName; + static const char* disableWhenDisarmedName; static const char* videoSourceNoVideo; static const char* videoDisabled; @@ -63,6 +71,12 @@ public: static const char* videoSourceRTSP; static const char* videoSourceTCP; +signals: + void streamConfiguredChanged (); + +private slots: + void _configChanged (QVariant value); + private: SettingsFact* _videoSourceFact; SettingsFact* _udpPortFact; @@ -75,6 +89,8 @@ private: SettingsFact* _maxVideoSizeFact; SettingsFact* _enableStorageLimitFact; SettingsFact* _rtspTimeoutFact; + SettingsFact* _streamEnabledFact; + SettingsFact* _disableWhenDisarmedFact; }; #endif diff --git a/src/Terrain.cc b/src/Terrain.cc index 9d4f620dd3e3fd56530157aa1690aa63b99af1cc..8a30ef62272167ce43c84619bd059a16b8685ea2 100644 --- a/src/Terrain.cc +++ b/src/Terrain.cc @@ -19,154 +19,89 @@ #include #include #include +#include #include -QGC_LOGGING_CATEGORY(TerrainLog, "TerrainLog") +QGC_LOGGING_CATEGORY(ElevationProviderLog, "ElevationProviderLog") -QMutex ElevationProvider::_tilesMutex; -QHash ElevationProvider::_tiles; -QStringList ElevationProvider::_downloadQueue; +Q_GLOBAL_STATIC(TerrainBatchManager, _terrainBatchManager) -ElevationProvider::ElevationProvider(QObject* parent) - : QObject(parent) +TerrainBatchManager::TerrainBatchManager(void) { - } -bool ElevationProvider::queryTerrainDataPoints(const QList& coordinates) +void TerrainBatchManager::addQuery(ElevationProvider* elevationProvider, const QList& coordinates) { - if (_state != State::Idle || coordinates.length() == 0) { - return false; - } - - QUrlQuery query; - QString points = ""; - for (const auto& coordinate : coordinates) { - points += QString::number(coordinate.latitude(), 'f', 10) + "," - + QString::number(coordinate.longitude(), 'f', 10) + ","; - } - points = points.mid(0, points.length() - 1); // remove the last ',' + if (coordinates.length() > 0) { + QList altitudes; - query.addQueryItem(QStringLiteral("points"), points); - QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele")); - url.setQuery(query); - - QNetworkRequest request(url); - - QNetworkProxy tProxy; - tProxy.setType(QNetworkProxy::DefaultProxy); - _networkManager.setProxy(tProxy); + if (!_getAltitudesForCoordinates(coordinates, altitudes)) { + QueuedRequestInfo_t queuedRequestInfo = { elevationProvider, coordinates }; + _requestQueue.append(queuedRequestInfo); + } - QNetworkReply* networkReply = _networkManager.get(request); - if (!networkReply) { - return false; + qCDebug(ElevationProviderLog) << "All altitudes taken from cached data"; + elevationProvider->_signalTerrainData(true, altitudes); } - - connect(networkReply, &QNetworkReply::finished, this, &ElevationProvider::_requestFinished); - - _state = State::Downloading; - return true; } -bool ElevationProvider::queryTerrainData(const QList& coordinates) +bool TerrainBatchManager::_getAltitudesForCoordinates(const QList& coordinates, QList& altitudes) { - if (_state != State::Idle || coordinates.length() == 0) { - return false; - } - - QList altitudes; - bool needToDownload = false; - foreach (QGeoCoordinate coordinate, coordinates) { + foreach (const QGeoCoordinate& coordinate, coordinates) { QString tileHash = _getTileHash(coordinate); _tilesMutex.lock(); if (!_tiles.contains(tileHash)) { - qCDebug(TerrainLog) << "Need to download tile " << tileHash; + qCDebug(ElevationProviderLog) << "Need to download tile " << tileHash; - if (!_downloadQueue.contains(tileHash)) { + if (!_tileDownloadQueue.contains(tileHash)) { // Schedule the fetch task - QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1, &_networkManager); - QGeoTileSpec spec; - spec.setX(QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1)); - spec.setY(QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1)); - spec.setZoom(1); - spec.setMapId(UrlFactory::AirmapElevation); - QGeoTiledMapReplyQGC* reply = new QGeoTiledMapReplyQGC(&_networkManager, request, spec); - connect(reply, &QGeoTiledMapReplyQGC::finished, this, &ElevationProvider::_fetchedTile); - connect(reply, &QGeoTiledMapReplyQGC::aborted, this, &ElevationProvider::_fetchedTile); - - _downloadQueue.append(tileHash); + if (_state != State::Downloading) { + QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1, &_networkManager); + QGeoTileSpec spec; + spec.setX(QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1)); + spec.setY(QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1)); + spec.setZoom(1); + spec.setMapId(UrlFactory::AirmapElevation); + QGeoTiledMapReplyQGC* reply = new QGeoTiledMapReplyQGC(&_networkManager, request, spec); + connect(reply, &QGeoTiledMapReplyQGC::finished, this, &TerrainBatchManager::_fetchedTile); + connect(reply, &QGeoTiledMapReplyQGC::aborted, this, &TerrainBatchManager::_fetchedTile); + _state = State::Downloading; + } + + _tileDownloadQueue.append(tileHash); } - needToDownload = true; + + return false; } else { - if (!needToDownload) { - if (_tiles[tileHash].isIn(coordinate)) { - altitudes.push_back(_tiles[tileHash].elevation(coordinate)); - } else { - qCDebug(TerrainLog) << "Error: coordinate not in tile region"; - altitudes.push_back(-1.0); - } + if (_tiles[tileHash].isIn(coordinate)) { + altitudes.push_back(_tiles[tileHash].elevation(coordinate)); + } else { + qCDebug(ElevationProviderLog) << "Error: coordinate not in tile region"; + altitudes.push_back(-1.0); } } _tilesMutex.unlock(); } - - if (!needToDownload) { - qCDebug(TerrainLog) << "All altitudes taken from cached data"; - emit terrainData(true, altitudes); - _coordinates.clear(); - return true; - } - - _coordinates = coordinates; - - return false; + return true; } -void ElevationProvider::_requestFinished() +void TerrainBatchManager::_tileFailed(void) { - QNetworkReply* reply = qobject_cast(QObject::sender()); - QList altitudes; - _state = State::Idle; - - // When an error occurs we still end up here - if (reply->error() != QNetworkReply::NoError) { - QByteArray responseBytes = reply->readAll(); - - QJsonParseError parseError; - QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); - qCDebug(TerrainLog) << "Received " << responseJson; - emit terrainData(false, altitudes); - return; - } - - QByteArray responseBytes = reply->readAll(); - - QJsonParseError parseError; - QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); - if (parseError.error != QJsonParseError::NoError) { - emit terrainData(false, altitudes); - return; - } - QJsonObject rootObject = responseJson.object(); - QString status = rootObject["status"].toString(); - if (status == "success") { - const QJsonArray& dataArray = rootObject["data"].toArray(); - for (int i = 0; i < dataArray.count(); i++) { - altitudes.push_back(dataArray[i].toDouble()); - } + QList noAltitudes; - emit terrainData(true, altitudes); + foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) { + requestInfo.elevationProvider->_signalTerrainData(false, noAltitudes); } - emit terrainData(false, altitudes); + _requestQueue.clear(); } -void ElevationProvider::_fetchedTile() +void TerrainBatchManager::_fetchedTile() { QGeoTiledMapReplyQGC* reply = qobject_cast(QObject::sender()); if (!reply) { - qCDebug(TerrainLog) << "Elevation tile fetched but invalid reply data type."; + qCDebug(ElevationProviderLog) << "Elevation tile fetched but invalid reply data type."; return; } @@ -174,23 +109,25 @@ void ElevationProvider::_fetchedTile() QGeoTileSpec spec = reply->tileSpec(); QString hash = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, spec.x(), spec.y(), spec.zoom()); _tilesMutex.lock(); - if (_downloadQueue.contains(hash)) { - _downloadQueue.removeOne(hash); + if (_tileDownloadQueue.contains(hash)) { + _tileDownloadQueue.removeOne(hash); + } else { + qCDebug(ElevationProviderLog) << "Loaded elevation tile, but not found in download queue."; } _tilesMutex.unlock(); // handle potential errors if (reply->error() != QGeoTiledMapReply::NoError) { if (reply->error() == QGeoTiledMapReply::CommunicationError) { - qCDebug(TerrainLog) << "Elevation tile fetching returned communication error. " << reply->errorString(); + qCDebug(ElevationProviderLog) << "Elevation tile fetching returned communication error. " << reply->errorString(); } else { - qCDebug(TerrainLog) << "Elevation tile fetching returned error. " << reply->errorString(); + qCDebug(ElevationProviderLog) << "Elevation tile fetching returned error. " << reply->errorString(); } reply->deleteLater(); return; } if (!reply->isFinished()) { - qCDebug(TerrainLog) << "Error in fetching elevation tile. Not finished. " << reply->errorString(); + qCDebug(ElevationProviderLog) << "Error in fetching elevation tile. Not finished. " << reply->errorString(); reply->deleteLater(); return; } @@ -201,8 +138,8 @@ void ElevationProvider::_fetchedTile() QJsonParseError parseError; QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); if (parseError.error != QJsonParseError::NoError) { - qCDebug(TerrainLog) << "Could not parse terrain tile " << parseError.errorString(); - qCDebug(TerrainLog) << responseBytes; + qCDebug(ElevationProviderLog) << "Could not parse terrain tile " << parseError.errorString(); + qCDebug(ElevationProviderLog) << responseBytes; reply->deleteLater(); return; } @@ -217,18 +154,41 @@ void ElevationProvider::_fetchedTile() } _tilesMutex.unlock(); } else { - qCDebug(TerrainLog) << "Received invalid tile"; + qCDebug(ElevationProviderLog) << "Received invalid tile"; } reply->deleteLater(); // now try to query the data again - queryTerrainData(_coordinates); + for (int i = _requestQueue.count() - 1; i >= 0; i--) { + QList altitudes; + if (_getAltitudesForCoordinates(_requestQueue[i].coordinates, altitudes)) { + _requestQueue[i].elevationProvider->_signalTerrainData(true, altitudes); + _requestQueue.removeAt(i); + } + } } -QString ElevationProvider::_getTileHash(const QGeoCoordinate& coordinate) +QString TerrainBatchManager::_getTileHash(const QGeoCoordinate& coordinate) { QString ret = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1); - qCDebug(TerrainLog) << "Computing unique tile hash for " << coordinate << ret; + qCDebug(ElevationProviderLog) << "Computing unique tile hash for " << coordinate << ret; return ret; } + +ElevationProvider::ElevationProvider(QObject* parent) + : QObject(parent) +{ + +} + +bool ElevationProvider::queryTerrainData(const QList& coordinates) +{ + if (coordinates.length() == 0) { + return false; + } + + _terrainBatchManager->addQuery(this, coordinates); + + return false; +} diff --git a/src/Terrain.cc.orig b/src/Terrain.cc.orig new file mode 100644 index 0000000000000000000000000000000000000000..27670b03d561ea096d4a9845db0e3c2ebd33db6d --- /dev/null +++ b/src/Terrain.cc.orig @@ -0,0 +1,378 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +#include "Terrain.h" +#include "QGCMapEngine.h" +#include "QGeoMapReplyQGC.h" + +#include +#include +#include +#include +#include +#include +#include +#include +<<<<<<< HEAD +#include + +QGC_LOGGING_CATEGORY(TerrainLog, "TerrainLog") + +QMutex ElevationProvider::_tilesMutex; +QHash ElevationProvider::_tiles; +QStringList ElevationProvider::_downloadQueue; +======= +#include +>>>>>>> pull_upstream + +QGC_LOGGING_CATEGORY(ElevationProviderLog, "ElevationProviderLog") + +Q_GLOBAL_STATIC(TerrainBatchManager, _terrainBatchManager) + +TerrainBatchManager::TerrainBatchManager(void) +{ + _batchTimer.setSingleShot(true); + _batchTimer.setInterval(_batchTimeout); + connect(&_batchTimer, &QTimer::timeout, this, &TerrainBatchManager::_sendNextBatch); +} + +void TerrainBatchManager::addQuery(ElevationProvider* elevationProvider, const QList& coordinates) +{ + if (coordinates.length() > 0) { + qCDebug(ElevationProviderLog) << "addQuery: elevationProvider:coordinates.count" << elevationProvider << coordinates.count(); + connect(elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); + QueuedRequestInfo_t queuedRequestInfo = { elevationProvider, coordinates }; + _requestQueue.append(queuedRequestInfo); + if (!_batchTimer.isActive()) { + _batchTimer.start(); + } + } +} + +<<<<<<< HEAD +bool ElevationProvider::queryTerrainDataPoints(const QList& coordinates) +======= +void TerrainBatchManager::_sendNextBatch(void) +>>>>>>> pull_upstream +{ + qCDebug(ElevationProviderLog) << "_sendNextBatch _state:_requestQueue.count:_sentRequests.count" << _stateToString(_state) << _requestQueue.count() << _sentRequests.count(); + + if (_state != State::Idle) { + // Waiting for last download the complete, wait some more + _batchTimer.start(); + return; + } + + if (_requestQueue.count() == 0) { + return; + } + + _sentRequests.clear(); + + // Convert coordinates to point strings for json query + QString points; + foreach (const QueuedRequestInfo_t& requestInfo, _requestQueue) { + SentRequestInfo_t sentRequestInfo = { requestInfo.elevationProvider, false, requestInfo.coordinates.count() }; + qCDebug(ElevationProviderLog) << "Building request: coordinate count" << requestInfo.coordinates.count(); + _sentRequests.append(sentRequestInfo); + + foreach (const QGeoCoordinate& coord, requestInfo.coordinates) { + points += QString::number(coord.latitude(), 'f', 10) + "," + + QString::number(coord.longitude(), 'f', 10) + ","; + } + + } + points = points.mid(0, points.length() - 1); // remove the last ',' from string + _requestQueue.clear(); + + QUrlQuery query; + query.addQueryItem(QStringLiteral("points"), points); + QUrl url(QStringLiteral("https://api.airmap.com/elevation/stage/srtm1/ele")); + url.setQuery(query); + + QNetworkRequest request(url); + + QNetworkProxy tProxy; + tProxy.setType(QNetworkProxy::DefaultProxy); + _networkManager.setProxy(tProxy); + + QNetworkReply* networkReply = _networkManager.get(request); + if (!networkReply) { + _batchFailed(); + return; + } + + connect(networkReply, &QNetworkReply::finished, this, &TerrainBatchManager::_requestFinished); + + _state = State::Downloading; +} + +<<<<<<< HEAD +bool ElevationProvider::queryTerrainData(const QList& coordinates) +{ + if (_state != State::Idle || coordinates.length() == 0) { + return false; + } + + QList altitudes; + bool needToDownload = false; + foreach (QGeoCoordinate coordinate, coordinates) { + QString tileHash = _getTileHash(coordinate); + _tilesMutex.lock(); + if (!_tiles.contains(tileHash)) { + qCDebug(TerrainLog) << "Need to download tile " << tileHash; + + if (!_downloadQueue.contains(tileHash)) { + // Schedule the fetch task + + QNetworkRequest request = getQGCMapEngine()->urlFactory()->getTileURL(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1, &_networkManager); + QGeoTileSpec spec; + spec.setX(QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1)); + spec.setY(QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1)); + spec.setZoom(1); + spec.setMapId(UrlFactory::AirmapElevation); + QGeoTiledMapReplyQGC* reply = new QGeoTiledMapReplyQGC(&_networkManager, request, spec); + connect(reply, &QGeoTiledMapReplyQGC::finished, this, &ElevationProvider::_fetchedTile); + connect(reply, &QGeoTiledMapReplyQGC::aborted, this, &ElevationProvider::_fetchedTile); + + _downloadQueue.append(tileHash); + } + needToDownload = true; + } else { + if (!needToDownload) { + if (_tiles[tileHash].isIn(coordinate)) { + altitudes.push_back(_tiles[tileHash].elevation(coordinate)); + } else { + qCDebug(TerrainLog) << "Error: coordinate not in tile region"; + altitudes.push_back(-1.0); + } + } + } + _tilesMutex.unlock(); + } + + if (!needToDownload) { + qCDebug(TerrainLog) << "All altitudes taken from cached data"; + emit terrainData(true, altitudes); + _coordinates.clear(); + return true; + } + + _coordinates = coordinates; + + return false; +} + +void ElevationProvider::_requestFinished() +======= +void TerrainBatchManager::_batchFailed(void) +{ + QList noAltitudes; + + foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { + if (!sentRequestInfo.providerDestroyed) { + disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); + sentRequestInfo.elevationProvider->_signalTerrainData(false, noAltitudes); + } + } + _sentRequests.clear(); +} + +void TerrainBatchManager::_requestFinished() +>>>>>>> pull_upstream +{ + QNetworkReply* reply = qobject_cast(QObject::sender()); + + _state = State::Idle; + + // When an error occurs we still end up here + if (reply->error() != QNetworkReply::NoError) { +<<<<<<< HEAD + QByteArray responseBytes = reply->readAll(); + + QJsonParseError parseError; + QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); + qCDebug(TerrainLog) << "Received " << responseJson; + emit terrainData(false, altitudes); +======= + qCDebug(ElevationProviderLog) << "_requestFinished error:" << reply->error(); + _batchFailed(); + reply->deleteLater(); +>>>>>>> pull_upstream + return; + } + + QByteArray responseBytes = reply->readAll(); + + QJsonParseError parseError; + QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qCDebug(ElevationProviderLog) << "_requestFinished unable to parse json:" << parseError.errorString(); + _batchFailed(); + reply->deleteLater(); + return; + } + + QJsonObject rootObject = responseJson.object(); + QString status = rootObject["status"].toString(); + if (status != "success") { + qCDebug(ElevationProviderLog) << "_requestFinished status != success:" << status; + _batchFailed(); + reply->deleteLater(); + return; + } + + QList altitudes; + const QJsonArray& dataArray = rootObject["data"].toArray(); + for (int i = 0; i < dataArray.count(); i++) { + altitudes.push_back(dataArray[i].toDouble()); + } + + int currentIndex = 0; + foreach (const SentRequestInfo_t& sentRequestInfo, _sentRequests) { + if (!sentRequestInfo.providerDestroyed) { + disconnect(sentRequestInfo.elevationProvider, &ElevationProvider::destroyed, this, &TerrainBatchManager::_elevationProviderDestroyed); + QList requestAltitudes = altitudes.mid(currentIndex, sentRequestInfo.cCoord); + sentRequestInfo.elevationProvider->_signalTerrainData(true, requestAltitudes); + currentIndex += sentRequestInfo.cCoord; + } + } + _sentRequests.clear(); + + reply->deleteLater(); +} + +void TerrainBatchManager::_elevationProviderDestroyed(QObject* elevationProvider) +{ + // Remove/Mark deleted objects queries from queues + + qCDebug(ElevationProviderLog) << "_elevationProviderDestroyed elevationProvider" << elevationProvider; + + int i = 0; + while (i < _requestQueue.count()) { + const QueuedRequestInfo_t& requestInfo = _requestQueue[i]; + if (requestInfo.elevationProvider == elevationProvider) { + qCDebug(ElevationProviderLog) << "Removing deleted provider from _requestQueue index:elevationProvider" << i << requestInfo.elevationProvider; + _requestQueue.removeAt(i); + } else { + i++; + } + } + + for (int i=0; i<_sentRequests.count(); i++) { + SentRequestInfo_t& sentRequestInfo = _sentRequests[i]; + if (sentRequestInfo.elevationProvider == elevationProvider) { + qCDebug(ElevationProviderLog) << "Zombieing deleted provider from _sentRequests index:elevatationProvider" << sentRequestInfo.elevationProvider; + sentRequestInfo.providerDestroyed = true; + } + } +} + +QString TerrainBatchManager::_stateToString(State state) +{ + switch (state) { + case State::Idle: + return QStringLiteral("Idle"); + case State::Downloading: + return QStringLiteral("Downloading"); + } + + return QStringLiteral("State unknown"); +} + +ElevationProvider::ElevationProvider(QObject* parent) + : QObject(parent) +{ + +} +void ElevationProvider::queryTerrainData(const QList& coordinates) +{ + if (coordinates.length() == 0) { + return; + } + + _terrainBatchManager->addQuery(this, coordinates); +} + +void ElevationProvider::_signalTerrainData(bool success, QList& altitudes) +{ + emit terrainData(success, altitudes); +} + +void ElevationProvider::_fetchedTile() +{ + QGeoTiledMapReplyQGC* reply = qobject_cast(QObject::sender()); + + if (!reply) { + qCDebug(TerrainLog) << "Elevation tile fetched but invalid reply data type."; + return; + } + + // remove from download queue + QGeoTileSpec spec = reply->tileSpec(); + QString hash = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, spec.x(), spec.y(), spec.zoom()); + _tilesMutex.lock(); + if (_downloadQueue.contains(hash)) { + _downloadQueue.removeOne(hash); + } + _tilesMutex.unlock(); + + // handle potential errors + if (reply->error() != QGeoTiledMapReply::NoError) { + if (reply->error() == QGeoTiledMapReply::CommunicationError) { + qCDebug(TerrainLog) << "Elevation tile fetching returned communication error. " << reply->errorString(); + } else { + qCDebug(TerrainLog) << "Elevation tile fetching returned error. " << reply->errorString(); + } + reply->deleteLater(); + return; + } + if (!reply->isFinished()) { + qCDebug(TerrainLog) << "Error in fetching elevation tile. Not finished. " << reply->errorString(); + reply->deleteLater(); + return; + } + + // parse received data and insert into hash table + QByteArray responseBytes = reply->mapImageData(); + + QJsonParseError parseError; + QJsonDocument responseJson = QJsonDocument::fromJson(responseBytes, &parseError); + if (parseError.error != QJsonParseError::NoError) { + qCDebug(TerrainLog) << "Could not parse terrain tile " << parseError.errorString(); + qCDebug(TerrainLog) << responseBytes; + reply->deleteLater(); + return; + } + + TerrainTile* terrainTile = new TerrainTile(responseJson); + if (terrainTile->isValid()) { + _tilesMutex.lock(); + if (!_tiles.contains(hash)) { + _tiles.insert(hash, *terrainTile); + } else { + delete terrainTile; + } + _tilesMutex.unlock(); + } else { + qCDebug(TerrainLog) << "Received invalid tile"; + } + reply->deleteLater(); + + // now try to query the data again + queryTerrainData(_coordinates); +} + +QString ElevationProvider::_getTileHash(const QGeoCoordinate& coordinate) +{ + QString ret = QGCMapEngine::getTileHash(UrlFactory::AirmapElevation, QGCMapEngine::long2elevationTileX(coordinate.longitude(), 1), QGCMapEngine::lat2elevationTileY(coordinate.latitude(), 1), 1); + qCDebug(TerrainLog) << "Computing unique tile hash for " << coordinate << ret; + + return ret; +} diff --git a/src/Terrain.h b/src/Terrain.h index 9fe154fe266dd3ab0e7001e4e7d6f01c0a0b2550..922047a660b8318762a980cf8125a2ddc111df80 100644 --- a/src/Terrain.h +++ b/src/Terrain.h @@ -30,7 +30,46 @@ p->queryTerrainData(coordinates); */ -Q_DECLARE_LOGGING_CATEGORY(TerrainLog) +Q_DECLARE_LOGGING_CATEGORY(ElevationProviderLog) + +class ElevationProvider; + +/// Used internally by ElevationProvider to batch requests together +class TerrainBatchManager : public QObject { + Q_OBJECT + +public: + TerrainBatchManager(void); + + void addQuery(ElevationProvider* elevationProvider, const QList& coordinates); + +private slots: + void _requestFinished (void); + void _fetchedTile (void); /// slot to handle fetched elevation tiles + +private: + typedef struct { + ElevationProvider* elevationProvider; + QList coordinates; + } QueuedRequestInfo_t; + + enum class State { + Idle, + Downloading, + }; + + void _tileFailed(void); + bool _getAltitudesForCoordinates(const QList& coordinates, QList& altitudes); + QString _getTileHash(const QGeoCoordinate& coordinate); /// Method to create a unique string for each tile + + QList _requestQueue; + State _state = State::Idle; + QNetworkAccessManager _networkManager; + + QMutex _tilesMutex; + QHash _tiles; + QStringList _tileDownloadQueue; +}; class ElevationProvider : public QObject { @@ -38,14 +77,6 @@ class ElevationProvider : public QObject public: ElevationProvider(QObject* parent = NULL); - /** - * Async elevation query for a list of lon,lat coordinates. When the query is done, the terrainData() signal - * is emitted. This call directly looks elevations up online. - * @param coordinates - * @return true on success - */ - bool queryTerrainDataPoints(const QList& coordinates); - /** * Async elevation query for a list of lon,lat coordinates. When the query is done, the terrainData() signal * is emitted. This call caches local elevation tables for faster lookup in the future. @@ -54,28 +85,10 @@ public: */ bool queryTerrainData(const QList& coordinates); + /// Internal method + void _signalTerrainData(bool success, QList& altitudes); + signals: /// signal returning requested elevation data void terrainData(bool success, QList altitudes); - -private slots: - void _requestFinished(); /// slot to handle download of elevation of list of coordinates - void _fetchedTile(); /// slot to handle fetched elevation tiles - -private: - - QString _getTileHash(const QGeoCoordinate& coordinate); /// Method to create a unique string for each tile - - enum class State { - Idle, - Downloading, - }; - - State _state = State::Idle; - QNetworkAccessManager _networkManager; - QList _coordinates; - - static QMutex _tilesMutex; - static QHash _tiles; - static QStringList _downloadQueue; }; diff --git a/src/UTM.cpp b/src/UTM.cpp new file mode 100644 index 0000000000000000000000000000000000000000..64e597095a66b59467afb66b55453b873e88e117 --- /dev/null +++ b/src/UTM.cpp @@ -0,0 +1,421 @@ +// UTM.c + +// Original Javascript by Chuck Taylor +// Port to C++ by Alex Hajnal +// +// *** THIS CODE USES 32-BIT FLOATS BY DEFAULT *** +// *** For 64-bit double-precision edit UTM.h: undefine FLOAT_32 and define FLOAT_64 +// +// This is a simple port of the code on the Geographic/UTM Coordinate Converter (1) page from Javascript to C++. +// Using this you can easily convert between UTM and WGS84 (latitude and longitude). +// Accuracy seems to be around 50cm (I suspect rounding errors are limiting precision). +// This code is provided as-is and has been minimally tested; enjoy but use at your own risk! +// The license for UTM.cpp and UTM.h is the same as the original Javascript: +// "The C++ source code in UTM.cpp and UTM.h may be copied and reused without restriction." +// +// 1) http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html + +#include "UTM.h" + +// DegToRad +// Converts degrees to radians. +FLOAT DegToRad(FLOAT deg) { + return (deg / 180.0 * pi); +} + + +// RadToDeg +// Converts radians to degrees. +FLOAT RadToDeg(FLOAT rad) { + return (rad / pi * 180.0); +} + +// ArcLengthOfMeridian +// Computes the ellipsoidal distance from the equator to a point at a +// given latitude. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// phi - Latitude of the point, in radians. +// +// Globals: +// sm_a - Ellipsoid model major axis. +// sm_b - Ellipsoid model minor axis. +// +// Returns: +// The ellipsoidal distance of the point from the equator, in meters. +FLOAT ArcLengthOfMeridian (FLOAT phi) { + FLOAT alpha, beta, gamma, delta, epsilon, n; + FLOAT result; + + /* Precalculate n */ + n = (sm_a - sm_b) / (sm_a + sm_b); + + /* Precalculate alpha */ + alpha = ((sm_a + sm_b) / 2.0) + * (1.0 + (POW(n, 2.0) / 4.0) + (POW(n, 4.0) / 64.0)); + + /* Precalculate beta */ + beta = (-3.0 * n / 2.0) + (9.0 * POW(n, 3.0) / 16.0) + + (-3.0 * POW(n, 5.0) / 32.0); + + /* Precalculate gamma */ + gamma = (15.0 * POW(n, 2.0) / 16.0) + + (-15.0 * POW(n, 4.0) / 32.0); + + /* Precalculate delta */ + delta = (-35.0 * POW(n, 3.0) / 48.0) + + (105.0 * POW(n, 5.0) / 256.0); + + /* Precalculate epsilon */ + epsilon = (315.0 * POW(n, 4.0) / 512.0); + + /* Now calculate the sum of the series and return */ + result = alpha + * (phi + (beta * SIN(2.0 * phi)) + + (gamma * SIN(4.0 * phi)) + + (delta * SIN(6.0 * phi)) + + (epsilon * SIN(8.0 * phi))); + + return result; +} + + + +// UTMCentralMeridian +// Determines the central meridian for the given UTM zone. +// +// Inputs: +// zone - An integer value designating the UTM zone, range [1,60]. +// +// Returns: +// The central meridian for the given UTM zone, in radians +// Range of the central meridian is the radian equivalent of [-177,+177]. +FLOAT UTMCentralMeridian(int zone) { + FLOAT cmeridian; + cmeridian = DegToRad(-183.0 + ((FLOAT)zone * 6.0)); + + return cmeridian; +} + + + +// FootpointLatitude +// +// Computes the footpoint latitude for use in converting transverse +// Mercator coordinates to ellipsoidal coordinates. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// y - The UTM northing coordinate, in meters. +// +// Returns: +// The footpoint latitude, in radians. +FLOAT FootpointLatitude(FLOAT y) { + FLOAT y_, alpha_, beta_, gamma_, delta_, epsilon_, n; + FLOAT result; + + /* Precalculate n (Eq. 10.18) */ + n = (sm_a - sm_b) / (sm_a + sm_b); + + /* Precalculate alpha_ (Eq. 10.22) */ + /* (Same as alpha in Eq. 10.17) */ + alpha_ = ((sm_a + sm_b) / 2.0) + * (1 + (POW(n, 2.0) / 4) + (POW(n, 4.0) / 64)); + + /* Precalculate y_ (Eq. 10.23) */ + y_ = y / alpha_; + + /* Precalculate beta_ (Eq. 10.22) */ + beta_ = (3.0 * n / 2.0) + (-27.0 * POW(n, 3.0) / 32.0) + + (269.0 * POW(n, 5.0) / 512.0); + + /* Precalculate gamma_ (Eq. 10.22) */ + gamma_ = (21.0 * POW(n, 2.0) / 16.0) + + (-55.0 * POW(n, 4.0) / 32.0); + + /* Precalculate delta_ (Eq. 10.22) */ + delta_ = (151.0 * POW(n, 3.0) / 96.0) + + (-417.0 * POW(n, 5.0) / 128.0); + + /* Precalculate epsilon_ (Eq. 10.22) */ + epsilon_ = (1097.0 * POW(n, 4.0) / 512.0); + + /* Now calculate the sum of the series (Eq. 10.21) */ + result = y_ + (beta_ * SIN(2.0 * y_)) + + (gamma_ * SIN(4.0 * y_)) + + (delta_ * SIN(6.0 * y_)) + + (epsilon_ * SIN(8.0 * y_)); + + return result; +} + + + +// MapLatLonToXY +// Converts a latitude/longitude pair to x and y coordinates in the +// Transverse Mercator projection. Note that Transverse Mercator is not +// the same as UTM; a scale factor is required to convert between them. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// phi - Latitude of the point, in radians. +// lambda - Longitude of the point, in radians. +// lambda0 - Longitude of the central meridian to be used, in radians. +// +// Outputs: +// x - The x coordinate of the computed point. +// y - The y coordinate of the computed point. +// +// Returns: +// The function does not return a value. +void MapLatLonToXY (FLOAT phi, FLOAT lambda, FLOAT lambda0, FLOAT &x, FLOAT &y) { + FLOAT N, nu2, ep2, t, t2, l; + FLOAT l3coef, l4coef, l5coef, l6coef, l7coef, l8coef; + //FLOAT tmp; // Unused + + /* Precalculate ep2 */ + ep2 = (POW(sm_a, 2.0) - POW(sm_b, 2.0)) / POW(sm_b, 2.0); + + /* Precalculate nu2 */ + nu2 = ep2 * POW(COS(phi), 2.0); + + /* Precalculate N */ + N = POW(sm_a, 2.0) / (sm_b * SQRT(1 + nu2)); + + /* Precalculate t */ + t = TAN(phi); + t2 = t * t; + //tmp = (t2 * t2 * t2) - POW(t, 6.0); // Unused + + /* Precalculate l */ + l = lambda - lambda0; + + /* Precalculate coefficients for l**n in the equations below + so a normal human being can read the expressions for easting + and northing + -- l**1 and l**2 have coefficients of 1.0 */ + l3coef = 1.0 - t2 + nu2; + + l4coef = 5.0 - t2 + 9 * nu2 + 4.0 * (nu2 * nu2); + + l5coef = 5.0 - 18.0 * t2 + (t2 * t2) + 14.0 * nu2 + - 58.0 * t2 * nu2; + + l6coef = 61.0 - 58.0 * t2 + (t2 * t2) + 270.0 * nu2 + - 330.0 * t2 * nu2; + + l7coef = 61.0 - 479.0 * t2 + 179.0 * (t2 * t2) - (t2 * t2 * t2); + + l8coef = 1385.0 - 3111.0 * t2 + 543.0 * (t2 * t2) - (t2 * t2 * t2); + + /* Calculate easting (x) */ + x = N * COS(phi) * l + + (N / 6.0 * POW(COS(phi), 3.0) * l3coef * POW(l, 3.0)) + + (N / 120.0 * POW(COS(phi), 5.0) * l5coef * POW(l, 5.0)) + + (N / 5040.0 * POW(COS(phi), 7.0) * l7coef * POW(l, 7.0)); + + /* Calculate northing (y) */ + y = ArcLengthOfMeridian (phi) + + (t / 2.0 * N * POW(COS(phi), 2.0) * POW(l, 2.0)) + + (t / 24.0 * N * POW(COS(phi), 4.0) * l4coef * POW(l, 4.0)) + + (t / 720.0 * N * POW(COS(phi), 6.0) * l6coef * POW(l, 6.0)) + + (t / 40320.0 * N * POW(COS(phi), 8.0) * l8coef * POW(l, 8.0)); + + return; +} + + + +// MapXYToLatLon +// Converts x and y coordinates in the Transverse Mercator projection to +// a latitude/longitude pair. Note that Transverse Mercator is not +// the same as UTM; a scale factor is required to convert between them. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// x - The easting of the point, in meters. +// y - The northing of the point, in meters. +// lambda0 - Longitude of the central meridian to be used, in radians. +// +// Outputs: +// phi - Latitude in radians. +// lambda - Longitude in radians. +// +// Returns: +// The function does not return a value. +// +// Remarks: +// The local variables Nf, nuf2, tf, and tf2 serve the same purpose as +// N, nu2, t, and t2 in MapLatLonToXY, but they are computed with respect +// to the footpoint latitude phif. +// +// x1frac, x2frac, x2poly, x3poly, etc. are to enhance readability and +// to optimize computations. +void MapXYToLatLon (FLOAT x, FLOAT y, FLOAT lambda0, FLOAT& phi, FLOAT& lambda) +{ + FLOAT phif, Nf, Nfpow, nuf2, ep2, tf, tf2, tf4, cf; + FLOAT x1frac, x2frac, x3frac, x4frac, x5frac, x6frac, x7frac, x8frac; + FLOAT x2poly, x3poly, x4poly, x5poly, x6poly, x7poly, x8poly; + + /* Get the value of phif, the footpoint latitude. */ + phif = FootpointLatitude (y); + + /* Precalculate ep2 */ + ep2 = (POW(sm_a, 2.0) - POW(sm_b, 2.0)) + / POW(sm_b, 2.0); + + /* Precalculate cos (phif) */ + cf = COS(phif); + + /* Precalculate nuf2 */ + nuf2 = ep2 * POW(cf, 2.0); + + /* Precalculate Nf and initialize Nfpow */ + Nf = POW(sm_a, 2.0) / (sm_b * SQRT(1 + nuf2)); + Nfpow = Nf; + + /* Precalculate tf */ + tf = TAN(phif); + tf2 = tf * tf; + tf4 = tf2 * tf2; + + /* Precalculate fractional coefficients for x**n in the equations + below to simplify the expressions for latitude and longitude. */ + x1frac = 1.0 / (Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**2) */ + x2frac = tf / (2.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**3) */ + x3frac = 1.0 / (6.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**4) */ + x4frac = tf / (24.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**5) */ + x5frac = 1.0 / (120.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**6) */ + x6frac = tf / (720.0 * Nfpow); + + Nfpow *= Nf; /* now equals Nf**7) */ + x7frac = 1.0 / (5040.0 * Nfpow * cf); + + Nfpow *= Nf; /* now equals Nf**8) */ + x8frac = tf / (40320.0 * Nfpow); + + /* Precalculate polynomial coefficients for x**n. + -- x**1 does not have a polynomial coefficient. */ + x2poly = -1.0 - nuf2; + + x3poly = -1.0 - 2 * tf2 - nuf2; + + x4poly = 5.0 + 3.0 * tf2 + 6.0 * nuf2 - 6.0 * tf2 * nuf2 + - 3.0 * (nuf2 *nuf2) - 9.0 * tf2 * (nuf2 * nuf2); + + x5poly = 5.0 + 28.0 * tf2 + 24.0 * tf4 + 6.0 * nuf2 + 8.0 * tf2 * nuf2; + + x6poly = -61.0 - 90.0 * tf2 - 45.0 * tf4 - 107.0 * nuf2 + + 162.0 * tf2 * nuf2; + + x7poly = -61.0 - 662.0 * tf2 - 1320.0 * tf4 - 720.0 * (tf4 * tf2); + + x8poly = 1385.0 + 3633.0 * tf2 + 4095.0 * tf4 + 1575 * (tf4 * tf2); + + /* Calculate latitude */ + phi = phif + x2frac * x2poly * (x * x) + + x4frac * x4poly * POW(x, 4.0) + + x6frac * x6poly * POW(x, 6.0) + + x8frac * x8poly * POW(x, 8.0); + + /* Calculate longitude */ + lambda = lambda0 + x1frac * x + + x3frac * x3poly * POW(x, 3.0) + + x5frac * x5poly * POW(x, 5.0) + + x7frac * x7poly * POW(x, 7.0); + + return; +} + + + + +// LatLonToUTMXY +// Converts a latitude/longitude pair to x and y coordinates in the +// Universal Transverse Mercator projection. +// +// Inputs: +// lat - Latitude of the point, in radians. +// lon - Longitude of the point, in radians. +// zone - UTM zone to be used for calculating values for x and y. +// If zone is less than 1 or greater than 60, the routine +// will determine the appropriate zone from the value of lon. +// +// Outputs: +// x - The x coordinate (easting) of the computed point. (in meters) +// y - The y coordinate (northing) of the computed point. (in meters) +// +// Returns: +// The UTM zone used for calculating the values of x and y. +int LatLonToUTMXY (FLOAT lat, FLOAT lon, int zone, FLOAT& x, FLOAT& y) { + if ( (zone < 1) || (zone > 60) ) + zone = FLOOR((lon + 180.0) / 6) + 1; + + MapLatLonToXY (DegToRad(lat), DegToRad(lon), UTMCentralMeridian(zone), x, y); + + /* Adjust easting and northing for UTM system. */ + x = x * UTMScaleFactor + 500000.0; + y = y * UTMScaleFactor; + if (y < 0.0) + y = y + 10000000.0; + + return zone; +} + + + +// UTMXYToLatLon +// +// Converts x and y coordinates in the Universal Transverse Mercator +// projection to a latitude/longitude pair. +// +// Inputs: +// x - The easting of the point, in meters. +// y - The northing of the point, in meters. +// zone - The UTM zone in which the point lies. +// southhemi - True if the point is in the southern hemisphere; +// false otherwise. +// +// Outputs: +// lat - The latitude of the point, in radians. +// lon - The longitude of the point, in radians. +// +// Returns: +// The function does not return a value. +void UTMXYToLatLon (FLOAT x, FLOAT y, int zone, bool southhemi, FLOAT& lat, FLOAT& lon) { + FLOAT cmeridian; + + x -= 500000.0; + x /= UTMScaleFactor; + + /* If in southern hemisphere, adjust y accordingly. */ + if (southhemi) + y -= 10000000.0; + + y /= UTMScaleFactor; + + cmeridian = UTMCentralMeridian (zone); + MapXYToLatLon (x, y, cmeridian, lat, lon); + + return; +} + diff --git a/src/UTM.h b/src/UTM.h new file mode 100644 index 0000000000000000000000000000000000000000..b7d9d9a360042513aa3ae4e77a58f46e206bbde3 --- /dev/null +++ b/src/UTM.h @@ -0,0 +1,206 @@ +// UTM.h + +// Original Javascript by Chuck Taylor +// Port to C++ by Alex Hajnal +// +// *** THIS CODE USES 32-BIT FLOATS BY DEFAULT *** +// *** For 64-bit double-precision edit this file: undefine FLOAT_32 and define FLOAT_64 (see below) +// +// This is a simple port of the code on the Geographic/UTM Coordinate Converter (1) page from Javascript to C++. +// Using this you can easily convert between UTM and WGS84 (latitude and longitude). +// Accuracy seems to be around 50cm (I suspect rounding errors are limiting precision). +// This code is provided as-is and has been minimally tested; enjoy but use at your own risk! +// The license for UTM.cpp and UTM.h is the same as the original Javascript: +// "The C++ source code in UTM.cpp and UTM.h may be copied and reused without restriction." +// +// 1) http://home.hiwaay.net/~taylorc/toolbox/geography/geoutm.html + +#ifndef UTM_H +#define UTM_H + +// Choose floating point precision: + +// 32-bit (for Teensy 3.5/3.6 ARM boards, etc.) +#define FLOAT_64 + +// 64-bit (for desktop/server use) +//#define FLOAT_64 + +#ifdef FLOAT_64 +#define FLOAT double +#define SIN sin +#define COS cos +#define TAN tan +#define POW pow +#define SQRT sqrt +#define FLOOR floor + +#else +#ifdef FLOAT_32 +#define FLOAT float +#define SIN sinf +#define COS cosf +#define TAN tanf +#define POW powf +#define SQRT sqrtf +#define FLOOR floorf + +#endif +#endif + + +#include + +#define pi 3.14159265358979 + +/* Ellipsoid model constants (actual values here are for WGS84) */ +#define sm_a 6378137.0 +#define sm_b 6356752.314 +#define sm_EccSquared 6.69437999013e-03 + +#define UTMScaleFactor 0.9996 + +// DegToRad +// Converts degrees to radians. +FLOAT DegToRad(FLOAT deg); + +// RadToDeg +// Converts radians to degrees. +FLOAT RadToDeg(FLOAT rad); + +// ArcLengthOfMeridian +// Computes the ellipsoidal distance from the equator to a point at a +// given latitude. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// phi - Latitude of the point, in radians. +// +// Globals: +// sm_a - Ellipsoid model major axis. +// sm_b - Ellipsoid model minor axis. +// +// Returns: +// The ellipsoidal distance of the point from the equator, in meters. +FLOAT ArcLengthOfMeridian (FLOAT phi); + +// UTMCentralMeridian +// Determines the central meridian for the given UTM zone. +// +// Inputs: +// zone - An integer value designating the UTM zone, range [1,60]. +// +// Returns: +// The central meridian for the given UTM zone, in radians +// Range of the central meridian is the radian equivalent of [-177,+177]. +FLOAT UTMCentralMeridian(int zone); + +// FootpointLatitude +// +// Computes the footpoint latitude for use in converting transverse +// Mercator coordinates to ellipsoidal coordinates. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// y - The UTM northing coordinate, in meters. +// +// Returns: +// The footpoint latitude, in radians. +FLOAT FootpointLatitude(FLOAT y); + +// MapLatLonToXY +// Converts a latitude/longitude pair to x and y coordinates in the +// Transverse Mercator projection. Note that Transverse Mercator is not +// the same as UTM; a scale factor is required to convert between them. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// phi - Latitude of the point, in radians. +// lambda - Longitude of the point, in radians. +// lambda0 - Longitude of the central meridian to be used, in radians. +// +// Outputs: +// x - The x coordinate of the computed point. +// y - The y coordinate of the computed point. +// +// Returns: +// The function does not return a value. +void MapLatLonToXY (FLOAT phi, FLOAT lambda, FLOAT lambda0, FLOAT &x, FLOAT &y); + +// MapXYToLatLon +// Converts x and y coordinates in the Transverse Mercator projection to +// a latitude/longitude pair. Note that Transverse Mercator is not +// the same as UTM; a scale factor is required to convert between them. +// +// Reference: Hoffmann-Wellenhof, B., Lichtenegger, H., and Collins, J., +// GPS: Theory and Practice, 3rd ed. New York: Springer-Verlag Wien, 1994. +// +// Inputs: +// x - The easting of the point, in meters. +// y - The northing of the point, in meters. +// lambda0 - Longitude of the central meridian to be used, in radians. +// +// Outputs: +// phi - Latitude in radians. +// lambda - Longitude in radians. +// +// Returns: +// The function does not return a value. +// +// Remarks: +// The local variables Nf, nuf2, tf, and tf2 serve the same purpose as +// N, nu2, t, and t2 in MapLatLonToXY, but they are computed with respect +// to the footpoint latitude phif. +// +// x1frac, x2frac, x2poly, x3poly, etc. are to enhance readability and +// to optimize computations. +void MapXYToLatLon (FLOAT x, FLOAT y, FLOAT lambda0, FLOAT& phi, FLOAT& lambda); + +// LatLonToUTMXY +// Converts a latitude/longitude pair to x and y coordinates in the +// Universal Transverse Mercator projection. +// +// Inputs: +// lat - Latitude of the point, in radians. +// lon - Longitude of the point, in radians. +// zone - UTM zone to be used for calculating values for x and y. +// If zone is less than 1 or greater than 60, the routine +// will determine the appropriate zone from the value of lon. +// +// Outputs: +// x - The x coordinate (easting) of the computed point. (in meters) +// y - The y coordinate (northing) of the computed point. (in meters) +// +// Returns: +// The UTM zone used for calculating the values of x and y. +int LatLonToUTMXY (FLOAT lat, FLOAT lon, int zone, FLOAT& x, FLOAT& y); + +// UTMXYToLatLon +// +// Converts x and y coordinates in the Universal Transverse Mercator// The UTM zone parameter should be in the range [1,60]. + +// projection to a latitude/longitude pair. +// +// Inputs: +// x - The easting of the point, in meters. +// y - The northing of the point, in meters. +// zone - The UTM zone in which the point lies. +// southhemi - True if the point is in the southern hemisphere; +// false otherwise. +// +// Outputs: +// lat - The latitude of the point, in radians. +// lon - The longitude of the point, in radians. +// +// Returns: +// The function does not return a value. +void UTMXYToLatLon (FLOAT x, FLOAT y, int zone, bool southhemi, FLOAT& lat, FLOAT& lon); + +#endif + diff --git a/src/Vehicle/BatteryFact.json b/src/Vehicle/BatteryFact.json index f6ca0c9b05b774492f50f353af31c407adf4f15c..52144355398bbfde975476e481f3256e042a6929 100644 --- a/src/Vehicle/BatteryFact.json +++ b/src/Vehicle/BatteryFact.json @@ -39,5 +39,12 @@ "shortDescription": "Cell Count", "type": "int32", "decimalPlaces": 0 +}, +{ + "name": "instantPower", + "shortDescription": "Watts", + "type": "float", + "decimalPlaces": 2, + "units": "W" } ] diff --git a/src/Vehicle/ClockFact.json b/src/Vehicle/ClockFact.json new file mode 100644 index 0000000000000000000000000000000000000000..dc233f9ca1003d8285accc84a216f69d447671bd --- /dev/null +++ b/src/Vehicle/ClockFact.json @@ -0,0 +1,12 @@ +[ +{ + "name": "currentTime", + "shortDescription": "Time", + "type": "string" +}, +{ + "name": "currentDate", + "shortDescription": "Date", + "type": "string" +} +] diff --git a/src/Vehicle/MultiVehicleManager.cc b/src/Vehicle/MultiVehicleManager.cc index 742052ccd2efd5c883ea7550bd77fa3e408a10d9..cbe4bdea6ee1eb4de6842f290ff453a9e73d7b2d 100644 --- a/src/Vehicle/MultiVehicleManager.cc +++ b/src/Vehicle/MultiVehicleManager.cc @@ -54,38 +54,38 @@ MultiVehicleManager::MultiVehicleManager(QGCApplication* app, QGCToolbox* toolbo void MultiVehicleManager::setToolbox(QGCToolbox *toolbox) { - QGCTool::setToolbox(toolbox); + QGCTool::setToolbox(toolbox); - _firmwarePluginManager = _toolbox->firmwarePluginManager(); - _joystickManager = _toolbox->joystickManager(); - _mavlinkProtocol = _toolbox->mavlinkProtocol(); + _firmwarePluginManager = _toolbox->firmwarePluginManager(); + _joystickManager = _toolbox->joystickManager(); + _mavlinkProtocol = _toolbox->mavlinkProtocol(); - QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); - qmlRegisterUncreatableType("QGroundControl.MultiVehicleManager", 1, 0, "MultiVehicleManager", "Reference only"); + QQmlEngine::setObjectOwnership(this, QQmlEngine::CppOwnership); + qmlRegisterUncreatableType("QGroundControl.MultiVehicleManager", 1, 0, "MultiVehicleManager", "Reference only"); - connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo); + connect(_mavlinkProtocol, &MAVLinkProtocol::vehicleHeartbeatInfo, this, &MultiVehicleManager::_vehicleHeartbeatInfo); - SettingsManager* settingsManager = toolbox->settingsManager(); - _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()), - static_cast(settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()), - _firmwarePluginManager, - this); + SettingsManager* settingsManager = toolbox->settingsManager(); + _offlineEditingVehicle = new Vehicle(static_cast(settingsManager->appSettings()->offlineEditingFirmwareType()->rawValue().toInt()), + static_cast(settingsManager->appSettings()->offlineEditingVehicleType()->rawValue().toInt()), + _firmwarePluginManager, + this); } -void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType) +void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleFirmwareType, int vehicleType) { if (componentId != MAV_COMP_ID_AUTOPILOT1) { - // Don't create vehicles for components other than the autopilot - if (!getVehicleById(vehicleId)) { + // Special case for PX4 Flow + if (vehicleId != 81 || componentId != 50) { + // Don't create vehicles for components other than the autopilot qCDebug(MultiVehicleManagerLog()) << "Ignoring heartbeat from unknown component " << link->getName() << vehicleId << componentId - << vehicleMavlinkVersion << vehicleFirmwareType << vehicleType; + return; } - return; } if (_vehicles.count() > 0 && !qgcApp()->toolbox()->corePlugin()->options()->multiVehicleEnabled()) { @@ -107,11 +107,10 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle break; } - qCDebug(MultiVehicleManagerLog()) << "Adding new vehicle link:vehicleId:componentId:vehicleMavlinkVersion:vehicleFirmwareType:vehicleType " + qCDebug(MultiVehicleManagerLog()) << "Adding new vehicle link:vehicleId:componentId:vehicleFirmwareType:vehicleType " << link->getName() << vehicleId << componentId - << vehicleMavlinkVersion << vehicleFirmwareType << vehicleType; @@ -119,16 +118,6 @@ void MultiVehicleManager::_vehicleHeartbeatInfo(LinkInterface* link, int vehicle _app->showMessage(tr("Warning: A vehicle is using the same system id as %1: %2").arg(qgcApp()->applicationName()).arg(vehicleId)); } -// QSettings settings; -// bool mavlinkVersionCheck = settings.value("VERSION_CHECK_ENABLED", true).toBool(); -// if (mavlinkVersionCheck && vehicleMavlinkVersion != MAVLINK_VERSION) { -// _ignoreVehicleIds += vehicleId; -// _app->showMessage(QString("The MAVLink protocol version on vehicle #%1 and %2 differ! " -// "It is unsafe to use different MAVLink versions. " -// "%2 therefore refuses to connect to vehicle #%1, which sends MAVLink version %3 (%2 uses version %4).").arg(vehicleId).arg(qgcApp()->applicationName()).arg(vehicleMavlinkVersion).arg(MAVLINK_VERSION)); -// return; -// } - Vehicle* vehicle = new Vehicle(link, vehicleId, componentId, (MAV_AUTOPILOT)vehicleFirmwareType, (MAV_TYPE)vehicleType, _firmwarePluginManager, _joystickManager); connect(vehicle, &Vehicle::allLinksInactive, this, &MultiVehicleManager::_deleteVehiclePhase1); connect(vehicle, &Vehicle::requestProtocolVersion, this, &MultiVehicleManager::_requestProtocolVersion); @@ -177,7 +166,7 @@ void MultiVehicleManager::_requestProtocolVersion(unsigned version) Vehicle *v = qobject_cast(_vehicles[i]); if (v && v->maxProtoVersion() > maxversion) { - maxversion = v->maxProtoVersion(); + maxversion = v->maxProtoVersion(); } } @@ -370,7 +359,7 @@ void MultiVehicleManager::_sendGCSHeartbeat(void) LinkManager* linkMgr = _toolbox->linkManager(); for (int i=0; ilinks().count(); i++) { LinkInterface* link = linkMgr->links()[i]; - if (link->isConnected()) { + if (link->isConnected() && !link->highLatency()) { mavlink_message_t message; mavlink_msg_heartbeat_pack_chan(_mavlinkProtocol->getSystemId(), _mavlinkProtocol->getComponentId(), diff --git a/src/Vehicle/MultiVehicleManager.h b/src/Vehicle/MultiVehicleManager.h index 5b2cfe6cabedad60c9dd76ff890ee806391d21ab..697d30d005e71c0faccf1f43f9fbb4dc61a57672 100644 --- a/src/Vehicle/MultiVehicleManager.h +++ b/src/Vehicle/MultiVehicleManager.h @@ -94,7 +94,7 @@ private slots: void _setActiveVehiclePhase2(void); void _vehicleParametersReadyChanged(bool parametersReady); void _sendGCSHeartbeat(void); - void _vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); + void _vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleFirmwareType, int vehicleType); void _requestProtocolVersion(unsigned version); private: diff --git a/src/Vehicle/SubmarineFact.json b/src/Vehicle/SubmarineFact.json index a32ae63d0a54a20eaa70c169d9e031672eec4e59..527a14f2a44575f6da98b48d27c263b49ab4c74c 100644 --- a/src/Vehicle/SubmarineFact.json +++ b/src/Vehicle/SubmarineFact.json @@ -28,5 +28,18 @@ "shortDescription": "Pilot Gain", "type": "int16", "units": "%" +}, +{ + "name": "input hold", + "shortDescription": "Input Hold", + "type": "int16", + "enumStrings": "Disabled,Enabled", + "enumValues": "0,1" +}, +{ "name": "rangefinder distance", + "shortDescription": "Rangefinder", + "type": "float", + "decimalPlaces": 2, + "units": "meters" } ] diff --git a/src/Vehicle/Vehicle.cc b/src/Vehicle/Vehicle.cc index 422637e5ab223974eaa81e8f4e09802195ee2dff..a05d86c8255aec861165578fadc8e2ebeb7091d5 100644 --- a/src/Vehicle/Vehicle.cc +++ b/src/Vehicle/Vehicle.cc @@ -7,6 +7,9 @@ * ****************************************************************************/ +#include +#include +#include #include "Vehicle.h" #include "MAVLinkProtocol.h" @@ -33,6 +36,8 @@ #include "QGCCorePlugin.h" #include "ADSBVehicle.h" #include "QGCCameraManager.h" +#include "VideoReceiver.h" +#include "VideoManager.h" QGC_LOGGING_CATEGORY(VehicleLog, "VehicleLog") @@ -64,6 +69,7 @@ const char* Vehicle::_batteryFactGroupName = "battery"; const char* Vehicle::_windFactGroupName = "wind"; const char* Vehicle::_vibrationFactGroupName = "vibration"; const char* Vehicle::_temperatureFactGroupName = "temperature"; +const char* Vehicle::_clockFactGroupName = "clock"; Vehicle::Vehicle(LinkInterface* link, int vehicleId, @@ -102,6 +108,7 @@ Vehicle::Vehicle(LinkInterface* link, , _autoDisconnect(false) , _flying(false) , _landing(false) + , _vtolInFwdFlight(false) , _onboardControlSensorsPresent(0) , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) @@ -120,6 +127,7 @@ Vehicle::Vehicle(LinkInterface* link, , _maxProtoVersion(0) , _vehicleCapabilitiesKnown(false) , _capabilityBits(0) + , _highLatencyLink(false) , _cameras(NULL) , _connectionLost(false) , _connectionLostEnabled(true) @@ -172,10 +180,12 @@ Vehicle::Vehicle(LinkInterface* link, , _windFactGroup(this) , _vibrationFactGroup(this) , _temperatureFactGroup(this) + , _clockFactGroup(this) { _addLink(link); - connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_activeJoystickChanged); + connect(_joystickManager, &JoystickManager::activeJoystickChanged, this, &Vehicle::_loadSettings); + connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleAvailableChanged, this, &Vehicle::_loadSettings); _mavlink = _toolbox->mavlinkProtocol(); @@ -211,7 +221,7 @@ Vehicle::Vehicle(LinkInterface* link, // Send MAV_CMD ack timer _mavCommandAckTimer.setSingleShot(true); - _mavCommandAckTimer.setInterval(_mavCommandAckTimeoutMSecs); + _mavCommandAckTimer.setInterval(_highLatencyLink ? _mavCommandAckTimeoutMSecsHighLatency : _mavCommandAckTimeoutMSecs); connect(&_mavCommandAckTimer, &QTimer::timeout, this, &Vehicle::_sendMavCommandAgain); _mav = uas(); @@ -223,19 +233,27 @@ Vehicle::Vehicle(LinkInterface* link, connect(_mav, SIGNAL(attitudeChanged (UASInterface*,double,double,double,quint64)), this, SLOT(_updateAttitude(UASInterface*, double, double, double, quint64))); connect(_mav, SIGNAL(attitudeChanged (UASInterface*,int,double,double,double,quint64)), this, SLOT(_updateAttitude(UASInterface*,int,double, double, double, quint64))); - _loadSettings(); - - // Ask the vehicle for protocol version info. - sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. - MAV_CMD_REQUEST_PROTOCOL_VERSION, - false, // No error shown if fails - 1); // Request protocol version + if (_highLatencyLink) { + // High latency links don't request information + _setMaxProtoVersion(100); + _setCapabilities(0); + _initialPlanRequestComplete = true; + _missionManagerInitialRequestSent = true; + _geoFenceManagerInitialRequestSent = true; + _rallyPointManagerInitialRequestSent = true; + } else { + // Ask the vehicle for protocol version info. + sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. + MAV_CMD_REQUEST_PROTOCOL_VERSION, + false, // No error shown if fails + 1); // Request protocol version - // Ask the vehicle for firmware version info. - sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. - MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES, - false, // No error shown if fails - 1); // Request firmware version + // Ask the vehicle for firmware version info. + sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. + MAV_CMD_REQUEST_AUTOPILOT_CAPABILITIES, + false, // No error shown if fails + 1); // Request firmware version + } _firmwarePlugin->initializeVehicle(this); @@ -285,6 +303,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _autoDisconnect(false) , _flying(false) , _landing(false) + , _vtolInFwdFlight(false) , _onboardControlSensorsPresent(0) , _onboardControlSensorsEnabled(0) , _onboardControlSensorsHealth(0) @@ -295,6 +314,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _defaultHoverSpeed(_settingsManager->appSettings()->offlineEditingHoverSpeed()->rawValue().toDouble()) , _vehicleCapabilitiesKnown(true) , _capabilityBits(_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA ? 0 : MAV_PROTOCOL_CAPABILITY_MISSION_FENCE | MAV_PROTOCOL_CAPABILITY_MISSION_RALLY) + , _highLatencyLink(false) , _cameras(NULL) , _connectionLost(false) , _connectionLostEnabled(true) @@ -346,6 +366,7 @@ Vehicle::Vehicle(MAV_AUTOPILOT firmwareType, , _batteryFactGroup(this) , _windFactGroup(this) , _vibrationFactGroup(this) + , _clockFactGroup(this) { _commonInit(); _firmwarePlugin->initializeVehicle(this); @@ -355,6 +376,8 @@ void Vehicle::_commonInit(void) { _firmwarePlugin = _firmwarePluginManager->firmwarePluginForAutopilot(_firmwareType, _vehicleType); + connect(_firmwarePlugin, &FirmwarePlugin::toolbarIndicatorsChanged, this, &Vehicle::toolBarIndicatorsChanged); + connect(this, &Vehicle::coordinateChanged, this, &Vehicle::_updateDistanceToHome); connect(this, &Vehicle::homePositionChanged, this, &Vehicle::_updateDistanceToHome); connect(this, &Vehicle::hobbsMeterChanged, this, &Vehicle::_updateHobbsMeter); @@ -407,6 +430,7 @@ void Vehicle::_commonInit(void) _addFactGroup(&_windFactGroup, _windFactGroupName); _addFactGroup(&_vibrationFactGroup, _vibrationFactGroupName); _addFactGroup(&_temperatureFactGroup, _temperatureFactGroupName); + _addFactGroup(&_clockFactGroup, _clockFactGroupName); // Add firmware-specific fact groups, if provided QMap* fwFactGroups = _firmwarePlugin->factGroups(); @@ -662,9 +686,6 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes break; case MAVLINK_MSG_ID_SCALED_PRESSURE3: _handleScaledPressure3(message); - break; - case MAVLINK_MSG_ID_CAMERA_FEEDBACK: - _handleCameraFeedback(message); break; case MAVLINK_MSG_ID_CAMERA_IMAGE_CAPTURED: _handleCameraImageCaptured(message); @@ -672,6 +693,9 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes case MAVLINK_MSG_ID_ADSB_VEHICLE: _handleADSBVehicle(message); break; + case MAVLINK_MSG_ID_HIGH_LATENCY2: + _handleHighLatency2(message); + break; case MAVLINK_MSG_ID_SERIAL_CONTROL: { @@ -680,11 +704,16 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes emit mavlinkSerialControl(ser.device, ser.flags, ser.timeout, ser.baudrate, QByteArray(reinterpret_cast(ser.data), ser.count)); } break; - // Following are ArduPilot dialect messages + // Following are ArduPilot dialect messages +#if !defined(NO_ARDUPILOT_DIALECT) + case MAVLINK_MSG_ID_CAMERA_FEEDBACK: + _handleCameraFeedback(message); + break; case MAVLINK_MSG_ID_WIND: _handleWind(message); break; +#endif } // This must be emitted after the vehicle processes the message. This way the vehicle state is up to date when anyone else @@ -695,6 +724,7 @@ void Vehicle::_mavlinkMessageReceived(LinkInterface* link, mavlink_message_t mes } +#if !defined(NO_ARDUPILOT_DIALECT) void Vehicle::_handleCameraFeedback(const mavlink_message_t& message) { mavlink_camera_feedback_t feedback; @@ -705,6 +735,7 @@ void Vehicle::_handleCameraFeedback(const mavlink_message_t& message) qCDebug(VehicleLog) << "_handleCameraFeedback coord:index" << imageCoordinate << feedback.img_idx; _cameraTriggerPoints.append(new QGCQGeoCoordinate(imageCoordinate, this)); } +#endif void Vehicle::_handleCameraImageCaptured(const mavlink_message_t& message) { @@ -778,6 +809,97 @@ void Vehicle::_handleGlobalPositionInt(mavlink_message_t& message) emit coordinateChanged(_coordinate); } +void Vehicle::_handleHighLatency2(mavlink_message_t& message) +{ + mavlink_high_latency2_t highLatency2; + mavlink_msg_high_latency2_decode(&message, &highLatency2); + + QString previousFlightMode; + if (_base_mode != 0 || _custom_mode != 0){ + // Vehicle is initialized with _base_mode=0 and _custom_mode=0. Don't pass this to flightMode() since it will complain about + // bad modes while unit testing. + previousFlightMode = flightMode(); + } + _base_mode = MAV_MODE_FLAG_CUSTOM_MODE_ENABLED; + _custom_mode = _firmwarePlugin->highLatencyCustomModeTo32Bits(highLatency2.custom_mode); + if (previousFlightMode != flightMode()) { + emit flightModeChanged(flightMode()); + } + + // Assume armed since we don't know + if (_armed != true) { + _armed = true; + emit armedChanged(_armed); + } + + _coordinate.setLatitude(highLatency2.latitude / (double)1E7); + _coordinate.setLongitude(highLatency2.longitude / (double)1E7); + _coordinate.setAltitude(highLatency2.altitude); + emit coordinateChanged(_coordinate); + + _airSpeedFact.setRawValue((double)highLatency2.airspeed / 5.0); + _groundSpeedFact.setRawValue((double)highLatency2.groundspeed / 5.0); + _climbRateFact.setRawValue((double)highLatency2.climb_rate / 10.0); + _headingFact.setRawValue((double)highLatency2.heading * 2.0); + _altitudeRelativeFact.setRawValue(std::numeric_limits::quiet_NaN()); + _altitudeAMSLFact.setRawValue(highLatency2.altitude); + + _windFactGroup.direction()->setRawValue((double)highLatency2.wind_heading * 2.0); + _windFactGroup.speed()->setRawValue((double)highLatency2.windspeed / 5.0); + + _batteryFactGroup.percentRemaining()->setRawValue(highLatency2.battery); + + _temperatureFactGroup.temperature1()->setRawValue(highLatency2.temperature_air); + + _gpsFactGroup.lat()->setRawValue(highLatency2.latitude * 1e-7); + _gpsFactGroup.lon()->setRawValue(highLatency2.longitude * 1e-7); + _gpsFactGroup.count()->setRawValue(0); + _gpsFactGroup.hdop()->setRawValue(highLatency2.eph == UINT8_MAX ? std::numeric_limits::quiet_NaN() : highLatency2.eph / 10.0); + _gpsFactGroup.vdop()->setRawValue(highLatency2.epv == UINT8_MAX ? std::numeric_limits::quiet_NaN() : highLatency2.epv / 10.0); + + struct failure2Sensor_s { + HL_FAILURE_FLAG failureBit; + MAV_SYS_STATUS_SENSOR sensorBit; + }; + + static const failure2Sensor_s rgFailure2Sensor[] = { + { HL_FAILURE_FLAG_GPS, MAV_SYS_STATUS_SENSOR_GPS }, + { HL_FAILURE_FLAG_DIFFERENTIAL_PRESSURE, MAV_SYS_STATUS_SENSOR_DIFFERENTIAL_PRESSURE }, + { HL_FAILURE_FLAG_ABSOLUTE_PRESSURE, MAV_SYS_STATUS_SENSOR_ABSOLUTE_PRESSURE }, + { HL_FAILURE_FLAG_3D_ACCEL, MAV_SYS_STATUS_SENSOR_3D_ACCEL }, + { HL_FAILURE_FLAG_3D_GYRO, MAV_SYS_STATUS_SENSOR_3D_GYRO }, + { HL_FAILURE_FLAG_3D_MAG, MAV_SYS_STATUS_SENSOR_3D_MAG }, +#if 0 + // FIXME: These don't currently map to existing sensor health bits. Support needs to be added to show them + // on health page of instrument panel as well. + { HL_FAILURE_FLAG_TERRAIN=64, /* Terrain subsystem failure. | */ + { HL_FAILURE_FLAG_BATTERY=128, /* Battery failure/critical low battery. | */ + { HL_FAILURE_FLAG_RC_RECEIVER=256, /* RC receiver failure/no rc connection. | */ + { HL_FAILURE_FLAG_OFFBOARD_LINK=512, /* Offboard link failure. | */ + { HL_FAILURE_FLAG_ENGINE=1024, /* Engine failure. | */ + { HL_FAILURE_FLAG_GEOFENCE=2048, /* Geofence violation. | */ + { HL_FAILURE_FLAG_ESTIMATOR=4096, /* Estimator failure, for example measurement rejection or large variances. | */ + { HL_FAILURE_FLAG_MISSION=8192, /* Mission failure. | */ +#endif + }; + + // Map from MAV_FAILURE bits to standard SYS_STATUS message handling + uint32_t newOnboardControlSensorsEnabled = 0; + for (size_t i=0; ifailureBit) { + // Assume if reporting as unhealthy that is it present and enabled + newOnboardControlSensorsEnabled |= pFailure2Sensor->sensorBit; + } + } + if (newOnboardControlSensorsEnabled != _onboardControlSensorsEnabled) { + _onboardControlSensorsEnabled = newOnboardControlSensorsEnabled; + _onboardControlSensorsPresent = newOnboardControlSensorsEnabled; + _onboardControlSensorsUnhealthy = 0; + emit unhealthySensorsChanged(); + } +} + void Vehicle::_handleAltitude(mavlink_message_t& message) { mavlink_altitude_t altitude; @@ -890,14 +1012,14 @@ QString Vehicle::vehicleUIDStr() QString uid; uint8_t* pUid = (uint8_t*)(void*)&_uid; uid.sprintf("%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X", - pUid[0] & 0xff, - pUid[1] & 0xff, - pUid[2] & 0xff, - pUid[3] & 0xff, - pUid[4] & 0xff, - pUid[5] & 0xff, - pUid[6] & 0xff, - pUid[7] & 0xff); + pUid[0] & 0xff, + pUid[1] & 0xff, + pUid[2] & 0xff, + pUid[3] & 0xff, + pUid[4] & 0xff, + pUid[5] & 0xff, + pUid[6] & 0xff, + pUid[7] & 0xff); return uid; } @@ -907,22 +1029,22 @@ void Vehicle::_handleHilActuatorControls(mavlink_message_t &message) mavlink_msg_hil_actuator_controls_decode(&message, &hil); emit hilActuatorControlsChanged(hil.time_usec, hil.flags, hil.controls[0], - hil.controls[1], - hil.controls[2], - hil.controls[3], - hil.controls[4], - hil.controls[5], - hil.controls[6], - hil.controls[7], - hil.controls[8], - hil.controls[9], - hil.controls[10], - hil.controls[11], - hil.controls[12], - hil.controls[13], - hil.controls[14], - hil.controls[15], - hil.mode); + hil.controls[1], + hil.controls[2], + hil.controls[3], + hil.controls[4], + hil.controls[5], + hil.controls[6], + hil.controls[7], + hil.controls[8], + hil.controls[9], + hil.controls[10], + hil.controls[11], + hil.controls[12], + hil.controls[13], + hil.controls[14], + hil.controls[15], + hil.mode); } void Vehicle::_handleCommandLong(mavlink_message_t& message) @@ -935,19 +1057,19 @@ void Vehicle::_handleCommandLong(mavlink_message_t& message) mavlink_msg_command_long_decode(&message, &cmd); switch (cmd.command) { - // Other component on the same system - // requests that QGC frees up the serial port of the autopilot - case MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN: - if (cmd.param6 > 0) { - // disconnect the USB link if its a direct connection to a Pixhawk - for (int i = 0; i < _links.length(); i++) { - SerialLink *sl = qobject_cast(_links.at(i)); - if (sl && sl->getSerialConfig()->usbDirect()) { - qDebug() << "Disconnecting:" << _links.at(i)->getName(); - qgcApp()->toolbox()->linkManager()->disconnectLink(_links.at(i)); - } + // Other component on the same system + // requests that QGC frees up the serial port of the autopilot + case MAV_CMD_PREFLIGHT_REBOOT_SHUTDOWN: + if (cmd.param6 > 0) { + // disconnect the USB link if its a direct connection to a Pixhawk + for (int i = 0; i < _links.length(); i++) { + SerialLink *sl = qobject_cast(_links.at(i)); + if (sl && sl->getSerialConfig()->usbDirect()) { + qDebug() << "Disconnecting:" << _links.at(i)->getName(); + qgcApp()->toolbox()->linkManager()->disconnectLink(_links.at(i)); } } + } break; } #endif @@ -975,6 +1097,14 @@ void Vehicle::_handleExtendedSysState(mavlink_message_t& message) default: break; } + + if (vtol()) { + bool vtolInFwdFlight = extendedState.vtol_state == MAV_VTOL_STATE_FW; + if (vtolInFwdFlight != _vtolInFwdFlight) { + _vtolInFwdFlight = vtolInFwdFlight; + emit vtolInFwdFlightChanged(vtolInFwdFlight); + } + } } void Vehicle::_handleVibration(mavlink_message_t& message) @@ -1003,6 +1133,7 @@ void Vehicle::_handleWindCov(mavlink_message_t& message) _windFactGroup.verticalSpeed()->setRawValue(0); } +#if !defined(NO_ARDUPILOT_DIALECT) void Vehicle::_handleWind(mavlink_message_t& message) { mavlink_wind_t wind; @@ -1012,6 +1143,7 @@ void Vehicle::_handleWind(mavlink_message_t& message) _windFactGroup.speed()->setRawValue(wind.speed); _windFactGroup.verticalSpeed()->setRawValue(wind.speed_z); } +#endif void Vehicle::_handleSysStatus(mavlink_message_t& message) { @@ -1028,13 +1160,15 @@ void Vehicle::_handleSysStatus(mavlink_message_t& message) _batteryFactGroup.voltage()->setRawValue(VehicleBatteryFactGroup::_voltageUnavailable); } else { _batteryFactGroup.voltage()->setRawValue((double)sysStatus.voltage_battery / 1000.0); + // current_battery is 10 mA and voltage_battery is 1mV. (10/1e3 times 1/1e3 = 1/1e5) + _batteryFactGroup.instantPower()->setRawValue((float)(sysStatus.current_battery*sysStatus.voltage_battery)/(100000.0)); } _batteryFactGroup.percentRemaining()->setRawValue(sysStatus.battery_remaining); if (sysStatus.battery_remaining > 0) { if (sysStatus.battery_remaining < _settingsManager->appSettings()->batteryPercentRemainingAnnounce()->rawValue().toInt() && sysStatus.battery_remaining < _lastAnnouncedLowBatteryPercent) { - _say(QString("%1 low battery: %2 percent remaining").arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); + _say(QString(tr("%1 low battery: %2 percent remaining")).arg(_vehicleIdSpeech()).arg(sysStatus.battery_remaining)); } _lastAnnouncedLowBatteryPercent = sysStatus.battery_remaining; } @@ -1121,6 +1255,11 @@ void Vehicle::_handleHeartbeat(mavlink_message_t& message) _clearCameraTriggerPoints(); } else { _mapTrajectoryStop(); + // Also handle Video Streaming + if(_settingsManager->videoSettings()->disableWhenDisarmed()->rawValue().toBool()) { + _settingsManager->videoSettings()->streamEnabled()->setRawValue(false); + qgcApp()->toolbox()->videoManager()->videoReceiver()->stop(); + } } } @@ -1315,8 +1454,10 @@ void Vehicle::_addLink(LinkInterface* link) qCDebug(VehicleLog) << "_addLink:" << QString("%1").arg((ulong)link, 0, 16); _links += link; _updatePriorityLink(); + _updateHighLatencyLink(); connect(_toolbox->linkManager(), &LinkManager::linkInactive, this, &Vehicle::_linkInactiveOrDeleted); connect(_toolbox->linkManager(), &LinkManager::linkDeleted, this, &Vehicle::_linkInactiveOrDeleted); + connect(link, &LinkInterface::highLatencyChanged, this, &Vehicle::_updateHighLatencyLink); } } @@ -1375,38 +1516,68 @@ void Vehicle::_updatePriorityLink(void) { LinkInterface* newPriorityLink = NULL; -#ifndef NO_SERIAL_LINK - // Note that this routine specificallty does not clear _priorityLink when there are no links remaining. + // This routine specifically does not clear _priorityLink when there are no links remaining. // By doing this we hold a reference on the last link as the Vehicle shuts down. Thus preventing shutdown // ordering NULL pointer crashes where priorityLink() is still called during shutdown sequence. + if (_links.count() == 0) { + return; + } + + // Check for the existing priority link to still be valid + for (int i=0; i<_links.count(); i++) { + if (_priorityLink.data() == _links[i]) { + if (!_priorityLink.data()->highLatency()) { + // Link is still valid. Continue to use it unless it is high latency. In that case we still look for a better + // link to use as priority link. + return; + } + } + } + + // The previous priority link is no longer valid. We must no find the best link available in this priority order: + // Direct USB connection + // Not a high latency link + // Any link + +#ifndef NO_SERIAL_LINK + // Search for direct usb connection for (int i=0; i<_links.count(); i++) { LinkInterface* link = _links[i]; - if (link->isConnected()) { - SerialLink* pSerialLink = qobject_cast(link); - if (pSerialLink) { - LinkConfiguration* config = pSerialLink->getLinkConfiguration(); - if (config) { - SerialConfiguration* pSerialConfig = qobject_cast(config); - if (pSerialConfig && pSerialConfig->usbDirect()) { - if (_priorityLink.data() != link) { - newPriorityLink = link; - break; - } - return; + SerialLink* pSerialLink = qobject_cast(link); + if (pSerialLink) { + LinkConfiguration* config = pSerialLink->getLinkConfiguration(); + if (config) { + SerialConfiguration* pSerialConfig = qobject_cast(config); + if (pSerialConfig && pSerialConfig->usbDirect()) { + if (_priorityLink.data() != link) { + newPriorityLink = link; + break; } + return; } } } } #endif - if (!newPriorityLink && !_priorityLink.data() && _links.count()) { - newPriorityLink = _links[0]; + if (!newPriorityLink) { + // Search for non-high latency link + for (int i=0; i<_links.count(); i++) { + LinkInterface* link = _links[i]; + if (!link->highLatency()) { + newPriorityLink = link; + break; + } + } } - if (newPriorityLink) { - _priorityLink = _toolbox->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink); + if (!newPriorityLink) { + // Use any link + newPriorityLink = _links[0]; } + + _priorityLink = _toolbox->linkManager()->sharedLinkInterfacePointerForLink(newPriorityLink); + _updateHighLatencyLink(); } void Vehicle::_updateAttitude(UASInterface*, double roll, double pitch, double yaw, quint64) @@ -1593,6 +1764,10 @@ int Vehicle::manualControlReservedButtonCount(void) void Vehicle::_loadSettings(void) { + if (!_active) { + return; + } + QSettings settings; settings.beginGroup(QString(_settingsGroup).arg(_id)); @@ -1651,12 +1826,6 @@ QStringList Vehicle::joystickModes(void) return list; } -void Vehicle::_activeJoystickChanged(void) -{ - _loadSettings(); - _startJoystick(true); -} - bool Vehicle::joystickEnabled(void) { return _joystickEnabled; @@ -1695,8 +1864,6 @@ void Vehicle::setActive(bool active) _active = active; emit activeChanged(_active); } - - _startJoystick(_active); } QGeoCoordinate Vehicle::homePosition(void) @@ -1968,7 +2135,6 @@ void Vehicle::_parametersReady(bool parametersReady) if (parametersReady) { _setupAutoDisarmSignalling(); _startPlanRequest(); - setJoystickEnabled(_joystickEnabled); } } @@ -2034,12 +2200,17 @@ void Vehicle::setConnectionLostEnabled(bool connectionLostEnabled) void Vehicle::_connectionLostTimeout(void) { + if (highLatencyLink()) { + // No connection timeout on high latency links + return; + } + if (_connectionLostEnabled && !_connectionLost) { _connectionLost = true; _heardFrom = false; _maxProtoVersion = 0; emit connectionLostChanged(true); - _say(QString("%1 communication lost").arg(_vehicleIdSpeech())); + _say(QString(tr("%1 communication lost")).arg(_vehicleIdSpeech())); if (_autoDisconnect) { // Reset link state @@ -2057,13 +2228,13 @@ void Vehicle::_connectionActive(void) if (_connectionLost) { _connectionLost = false; emit connectionLostChanged(false); - _say(QString("%1 communication regained").arg(_vehicleIdSpeech())); + _say(QString(tr("%1 communication regained")).arg(_vehicleIdSpeech())); // Re-negotiate protocol version for the link sendMavCommand(MAV_COMP_ID_ALL, // Don't know default component id yet. - MAV_CMD_REQUEST_PROTOCOL_VERSION, + MAV_CMD_REQUEST_PROTOCOL_VERSION, false, // No error shown if fails - 1); // Request protocol version + 1); // Request protocol version } } @@ -2160,7 +2331,7 @@ QString Vehicle::vehicleTypeName() const { QString Vehicle::_vehicleIdSpeech(void) { if (_toolbox->multiVehicleManager()->vehicles()->count() > 1) { - return QString("vehicle %1").arg(id()); + return QString(tr("vehicle %1")).arg(id()); } else { return QString(); } @@ -2168,13 +2339,13 @@ QString Vehicle::_vehicleIdSpeech(void) void Vehicle::_handleFlightModeChanged(const QString& flightMode) { - _say(QString("%1 %2 flight mode").arg(_vehicleIdSpeech()).arg(flightMode)); + _say(QString(tr("%1 %2 flight mode")).arg(_vehicleIdSpeech()).arg(flightMode)); emit guidedModeChanged(_firmwarePlugin->isGuidedMode(this)); } void Vehicle::_announceArmedChanged(bool armed) { - _say(QString("%1 %2").arg(_vehicleIdSpeech()).arg(armed ? QStringLiteral("armed") : QStringLiteral("disarmed"))); + _say(QString("%1 %2").arg(_vehicleIdSpeech()).arg(armed ? QString(tr("armed")) : QString(tr("disarmed")))); } void Vehicle::_setFlying(bool flying) @@ -2241,6 +2412,11 @@ void Vehicle::guidedModeTakeoff(double altitudeRelative) _firmwarePlugin->guidedModeTakeoff(this, altitudeRelative); } +double Vehicle::minimumTakeoffAltitude(void) +{ + return _firmwarePlugin->minimumTakeoffAltitude(this); +} + void Vehicle::startMission(void) { _firmwarePlugin->startMission(this); @@ -2576,7 +2752,7 @@ void Vehicle::setSoloFirmware(bool soloFirmware) } #if 0 - // Temporarily removed, waiting for new command implementation +// Temporarily removed, waiting for new command implementation void Vehicle::motorTest(int motor, int percent, int timeoutSecs) { doCommandLongUnverified(_defaultComponentId, MAV_CMD_DO_MOTOR_TEST, motor, MOTOR_TEST_THROTTLE_PERCENT, percent, timeoutSecs); @@ -2628,6 +2804,7 @@ QStringList Vehicle::unhealthySensors(void) const { MAV_SYS_STATUS_TERRAIN, "Terrain" }, { MAV_SYS_STATUS_REVERSE_MOTOR, "Motors reversed" }, { MAV_SYS_STATUS_LOGGING, "Logging" }, + { MAV_SYS_STATUS_SENSOR_BATTERY, "Battery" }, }; for (size_t i=0; igetSystemId(), - _mavlink->getComponentId(), - priorityLink()->mavlinkChannel(), - &msg, - &ack); + _mavlink->getSystemId(), + _mavlink->getComponentId(), + priorityLink()->mavlinkChannel(), + &msg, + &ack); sendMessageOnLink(priorityLink(), msg); } @@ -2725,7 +2913,7 @@ void Vehicle::_handleMavlinkLoggingData(mavlink_message_t& message) mavlink_logging_data_t log; mavlink_msg_logging_data_decode(&message, &log); emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, - log.first_message_offset, QByteArray((const char*)log.data, log.length), false); + log.first_message_offset, QByteArray((const char*)log.data, log.length), false); } void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) @@ -2734,7 +2922,7 @@ void Vehicle::_handleMavlinkLoggingDataAcked(mavlink_message_t& message) mavlink_msg_logging_data_acked_decode(&message, &log); _ackMavlinkLogData(log.sequence); emit mavlinkLogData(this, log.target_system, log.target_component, log.sequence, - log.first_message_offset, QByteArray((const char*)log.data, log.length), true); + log.first_message_offset, QByteArray((const char*)log.data, log.length), true); } void Vehicle::setFirmwarePluginInstanceData(QObject* firmwarePluginInstanceData) @@ -2893,7 +3081,7 @@ QString Vehicle::hobbsMeter() static const char* HOOBS_LO = "LND_FLIGHT_T_LO"; //-- TODO: Does this exist on non PX4? if (_parameterManager->parameterExists(FactSystem::defaultComponentId, HOOBS_HI) && - _parameterManager->parameterExists(FactSystem::defaultComponentId, HOOBS_LO)) { + _parameterManager->parameterExists(FactSystem::defaultComponentId, HOOBS_LO)) { Fact* factHi = _parameterManager->getParameter(FactSystem::defaultComponentId, HOOBS_HI); Fact* factLo = _parameterManager->getParameter(FactSystem::defaultComponentId, HOOBS_LO); uint64_t hobbsTimeSeconds = ((uint64_t)factHi->rawValue().toUInt() << 32 | (uint64_t)factLo->rawValue().toUInt()) / 1000000; @@ -2917,6 +3105,15 @@ void Vehicle::_vehicleParamLoaded(bool ready) } } +void Vehicle::_updateHighLatencyLink(void) +{ + if (_priorityLink->highLatency() != _highLatencyLink) { + _highLatencyLink = _priorityLink->highLatency(); + _mavCommandAckTimer.setInterval(_highLatencyLink ? _mavCommandAckTimeoutMSecsHighLatency : _mavCommandAckTimeoutMSecs); + emit highLatencyLinkChanged(_highLatencyLink); + } +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- @@ -2926,6 +3123,7 @@ const char* VehicleBatteryFactGroup::_mahConsumedFactName = "mah const char* VehicleBatteryFactGroup::_currentFactName = "current"; const char* VehicleBatteryFactGroup::_temperatureFactName = "temperature"; const char* VehicleBatteryFactGroup::_cellCountFactName = "cellCount"; +const char* VehicleBatteryFactGroup::_instantPowerFactName = "instantPower"; const char* VehicleBatteryFactGroup::_settingsGroup = "Vehicle.battery"; @@ -2935,6 +3133,7 @@ const int VehicleBatteryFactGroup::_mahConsumedUnavailable = -1; const int VehicleBatteryFactGroup::_currentUnavailable = -1; const double VehicleBatteryFactGroup::_temperatureUnavailable = -1.0; const int VehicleBatteryFactGroup::_cellCountUnavailable = -1.0; +const double VehicleBatteryFactGroup::_instantPowerUnavailable = -1.0; VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) : FactGroup(1000, ":/json/Vehicle/BatteryFact.json", parent) @@ -2944,6 +3143,7 @@ VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) , _currentFact (0, _currentFactName, FactMetaData::valueTypeFloat) , _temperatureFact (0, _temperatureFactName, FactMetaData::valueTypeDouble) , _cellCountFact (0, _cellCountFactName, FactMetaData::valueTypeInt32) + , _instantPowerFact (0, _instantPowerFactName, FactMetaData::valueTypeFloat) { _addFact(&_voltageFact, _voltageFactName); _addFact(&_percentRemainingFact, _percentRemainingFactName); @@ -2951,6 +3151,7 @@ VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) _addFact(&_currentFact, _currentFactName); _addFact(&_temperatureFact, _temperatureFactName); _addFact(&_cellCountFact, _cellCountFactName); + _addFact(&_instantPowerFact, _instantPowerFactName); // Start out as not available _voltageFact.setRawValue (_voltageUnavailable); @@ -2959,6 +3160,7 @@ VehicleBatteryFactGroup::VehicleBatteryFactGroup(QObject* parent) _currentFact.setRawValue (_currentUnavailable); _temperatureFact.setRawValue (_temperatureUnavailable); _cellCountFact.setRawValue (_cellCountUnavailable); + _instantPowerFact.setRawValue (_instantPowerUnavailable); } const char* VehicleWindFactGroup::_directionFactName = "direction"; @@ -3030,3 +3232,27 @@ VehicleTemperatureFactGroup::VehicleTemperatureFactGroup(QObject* parent) _temperature2Fact.setRawValue (std::numeric_limits::quiet_NaN()); _temperature3Fact.setRawValue (std::numeric_limits::quiet_NaN()); } + +const char* VehicleClockFactGroup::_currentTimeFactName = "currentTime"; +const char* VehicleClockFactGroup::_currentDateFactName = "currentDate"; + +VehicleClockFactGroup::VehicleClockFactGroup(QObject* parent) + : FactGroup(1000, ":/json/Vehicle/ClockFact.json", parent) + , _currentTimeFact (0, _currentTimeFactName, FactMetaData::valueTypeString) + , _currentDateFact (0, _currentDateFactName, FactMetaData::valueTypeString) +{ + _addFact(&_currentTimeFact, _currentTimeFactName); + _addFact(&_currentDateFact, _currentDateFactName); + + // Start out as not available "--.--" + _currentTimeFact.setRawValue (std::numeric_limits::quiet_NaN()); + _currentDateFact.setRawValue (std::numeric_limits::quiet_NaN()); +} + +void VehicleClockFactGroup::_updateAllValues(void) +{ + _currentTimeFact.setRawValue(QTime::currentTime().toString()); + _currentDateFact.setRawValue(QDateTime::currentDateTime().toString(QLocale::system().dateFormat(QLocale::ShortFormat))); + + FactGroup::_updateAllValues(); +} diff --git a/src/Vehicle/Vehicle.h b/src/Vehicle/Vehicle.h index e1ff02c722ecce1dd86430fa5df11bb9dad67cae..ba867511334781bb2cd8a4251c59709ebc10fc23 100644 --- a/src/Vehicle/Vehicle.h +++ b/src/Vehicle/Vehicle.h @@ -156,6 +156,7 @@ public: Q_PROPERTY(Fact* current READ current CONSTANT) Q_PROPERTY(Fact* temperature READ temperature CONSTANT) Q_PROPERTY(Fact* cellCount READ cellCount CONSTANT) + Q_PROPERTY(Fact* instantPower READ instantPower CONSTANT) Fact* voltage (void) { return &_voltageFact; } Fact* percentRemaining (void) { return &_percentRemainingFact; } @@ -163,6 +164,7 @@ public: Fact* current (void) { return &_currentFact; } Fact* temperature (void) { return &_temperatureFact; } Fact* cellCount (void) { return &_cellCountFact; } + Fact* instantPower (void) { return &_instantPowerFact; } static const char* _voltageFactName; @@ -171,6 +173,7 @@ public: static const char* _currentFactName; static const char* _temperatureFactName; static const char* _cellCountFactName; + static const char* _instantPowerFactName; static const char* _settingsGroup; @@ -180,6 +183,7 @@ public: static const int _currentUnavailable; static const double _temperatureUnavailable; static const int _cellCountUnavailable; + static const double _instantPowerUnavailable; private: Fact _voltageFact; @@ -188,6 +192,7 @@ private: Fact _currentFact; Fact _temperatureFact; Fact _cellCountFact; + Fact _instantPowerFact; }; class VehicleTemperatureFactGroup : public FactGroup @@ -219,6 +224,32 @@ private: Fact _temperature3Fact; }; +class VehicleClockFactGroup : public FactGroup +{ + Q_OBJECT + +public: + VehicleClockFactGroup(QObject* parent = NULL); + + Q_PROPERTY(Fact* currentTime READ currentTime CONSTANT) + Q_PROPERTY(Fact* currentDate READ currentDate CONSTANT) + + Fact* currentTime (void) { return &_currentTimeFact; } + Fact* currentDate (void) { return &_currentDateFact; } + + static const char* _currentTimeFactName; + static const char* _currentDateFactName; + + static const char* _settingsGroup; + +private slots: + void _updateAllValues(void) override; + +private: + Fact _currentTimeFact; + Fact _currentDateFact; +}; + class Vehicle : public FactGroup { Q_OBJECT @@ -314,12 +345,14 @@ public: Q_PROPERTY(unsigned int telemetryTXBuffer READ telemetryTXBuffer NOTIFY telemetryTXBufferChanged) Q_PROPERTY(int telemetryLNoise READ telemetryLNoise NOTIFY telemetryLNoiseChanged) Q_PROPERTY(int telemetryRNoise READ telemetryRNoise NOTIFY telemetryRNoiseChanged) - Q_PROPERTY(QVariantList toolBarIndicators READ toolBarIndicators CONSTANT) + Q_PROPERTY(QVariantList toolBarIndicators READ toolBarIndicators NOTIFY toolBarIndicatorsChanged) Q_PROPERTY(QmlObjectListModel* adsbVehicles READ adsbVehicles CONSTANT) Q_PROPERTY(bool initialPlanRequestComplete READ initialPlanRequestComplete NOTIFY initialPlanRequestCompleteChanged) Q_PROPERTY(QVariantList staticCameraList READ staticCameraList CONSTANT) Q_PROPERTY(QGCCameraManager* dynamicCameras READ dynamicCameras NOTIFY dynamicCamerasChanged) Q_PROPERTY(QString hobbsMeter READ hobbsMeter NOTIFY hobbsMeterChanged) + Q_PROPERTY(bool vtolInFwdFlight READ vtolInFwdFlight WRITE setVtolInFwdFlight NOTIFY vtolInFwdFlightChanged) + Q_PROPERTY(bool highLatencyLink READ highLatencyLink NOTIFY highLatencyLinkChanged) // Vehicle state used for guided control Q_PROPERTY(bool flying READ flying NOTIFY flyingChanged) ///< Vehicle is flying @@ -328,7 +361,7 @@ public: Q_PROPERTY(bool guidedModeSupported READ guidedModeSupported CONSTANT) ///< Guided mode commands are supported by this vehicle Q_PROPERTY(bool pauseVehicleSupported READ pauseVehicleSupported CONSTANT) ///< Pause vehicle command is supported Q_PROPERTY(bool orbitModeSupported READ orbitModeSupported CONSTANT) ///< Orbit mode is supported by this vehicle - Q_PROPERTY(bool takeoffVehicleSupported READ takeoffVehicleSupported CONSTANT) ///< Guided takeoff supported + Q_PROPERTY(bool takeoffVehicleSupported READ takeoffVehicleSupported CONSTANT) ///< Guided takeoff supported Q_PROPERTY(ParameterManager* parameterManager READ parameterManager CONSTANT) @@ -351,6 +384,7 @@ public: Q_PROPERTY(FactGroup* wind READ windFactGroup CONSTANT) Q_PROPERTY(FactGroup* vibration READ vibrationFactGroup CONSTANT) Q_PROPERTY(FactGroup* temperature READ temperatureFactGroup CONSTANT) + Q_PROPERTY(FactGroup* clock READ clockFactGroup CONSTANT) Q_PROPERTY(int firmwareMajorVersion READ firmwareMajorVersion NOTIFY firmwareVersionChanged) Q_PROPERTY(int firmwareMinorVersion READ firmwareMinorVersion NOTIFY firmwareVersionChanged) @@ -390,6 +424,9 @@ public: /// Command vehicle to takeoff from current location Q_INVOKABLE void guidedModeTakeoff(double altitudeRelative); + /// @return The minimum takeoff altitude (relative) for guided takeoff. + Q_INVOKABLE double minimumTakeoffAltitude(void); + /// Command vehicle to move to specified location (altitude is included and relative) Q_INVOKABLE void guidedModeGotoLocation(const QGeoCoordinate& gotoCoord); @@ -578,6 +615,7 @@ public: bool flying () const { return _flying; } bool landing () const { return _landing; } bool guidedMode () const; + bool vtolInFwdFlight () const { return _vtolInFwdFlight; } uint8_t baseMode () const { return _base_mode; } uint32_t customMode () const { return _custom_mode; } bool isOfflineEditingVehicle () const { return _offlineEditingVehicle; } @@ -601,6 +639,7 @@ public: int telemetryLNoise () { return _telemetryLNoise; } int telemetryRNoise () { return _telemetryRNoise; } bool autoDisarm (); + bool highLatencyLink () const { return _highLatencyLink; } /// Get the maximum MAVLink protocol version supported /// @return the maximum version unsigned maxProtoVersion () const { return _maxProtoVersion; } @@ -622,6 +661,7 @@ public: FactGroup* windFactGroup (void) { return &_windFactGroup; } FactGroup* vibrationFactGroup (void) { return &_vibrationFactGroup; } FactGroup* temperatureFactGroup (void) { return &_temperatureFactGroup; } + FactGroup* clockFactGroup (void) { return &_clockFactGroup; } void setConnectionLostEnabled(bool connectionLostEnabled); @@ -708,6 +748,7 @@ public: void _setFlying(bool flying); void _setLanding(bool landing); + void setVtolInFwdFlight(bool vtolInFwdFlight); void _setHomePosition(QGeoCoordinate& homeCoord); void _setMaxProtoVersion (unsigned version); @@ -733,6 +774,7 @@ signals: void flyingChanged(bool flying); void landingChanged(bool landing); void guidedModeChanged(bool guidedMode); + void vtolInFwdFlightChanged(bool vtolInFwdFlight); void prearmErrorChanged(const QString& prearmError); void soloFirmwareChanged(bool soloFirmware); void unhealthySensorsChanged(void); @@ -745,6 +787,8 @@ signals: void capabilitiesKnownChanged(bool capabilitiesKnown); void initialPlanRequestCompleteChanged(bool initialPlanRequestComplete); void capabilityBitsChanged(uint64_t capabilityBits); + void toolBarIndicatorsChanged(void); + void highLatencyLinkChanged(bool highLatencyLink); void messagesReceivedChanged (); void messagesSentChanged (); @@ -821,6 +865,7 @@ private slots: void _offlineVehicleTypeSettingChanged(QVariant value); void _offlineCruiseSpeedSettingChanged(QVariant value); void _offlineHoverSpeedSettingChanged(QVariant value); + void _updateHighLatencyLink(void); void _handleTextMessage (int newCount); void _handletextMessageReceived (UASMessage* message); @@ -836,7 +881,6 @@ private slots: void _geoFenceLoadComplete(void); void _rallyPointLoadComplete(void); void _sendMavCommandAgain(void); - void _activeJoystickChanged(void); void _clearTrajectoryPoints(void); void _clearCameraTriggerPoints(void); void _updateDistanceToHome(void); @@ -857,7 +901,6 @@ private: void _handleBatteryStatus(mavlink_message_t& message); void _handleSysStatus(mavlink_message_t& message); void _handleWindCov(mavlink_message_t& message); - void _handleWind(mavlink_message_t& message); void _handleVibration(mavlink_message_t& message); void _handleExtendedSysState(mavlink_message_t& message); void _handleCommandAck(mavlink_message_t& message); @@ -872,7 +915,12 @@ private: void _handleScaledPressure(mavlink_message_t& message); void _handleScaledPressure2(mavlink_message_t& message); void _handleScaledPressure3(mavlink_message_t& message); + void _handleHighLatency2(mavlink_message_t& message); + // ArduPilot dialect messages +#if !defined(NO_ARDUPILOT_DIALECT) void _handleCameraFeedback(const mavlink_message_t& message); + void _handleWind(mavlink_message_t& message); +#endif void _handleCameraImageCaptured(const mavlink_message_t& message); void _handleADSBVehicle(const mavlink_message_t& message); void _missionManagerError(int errorCode, const QString& errorMsg); @@ -933,6 +981,7 @@ private: bool _autoDisconnect; ///< true: Automatically disconnect vehicle when last connection goes away or lost heartbeat bool _flying; bool _landing; + bool _vtolInFwdFlight; uint32_t _onboardControlSensorsPresent; uint32_t _onboardControlSensorsEnabled; uint32_t _onboardControlSensorsHealth; @@ -951,6 +1000,7 @@ private: unsigned _maxProtoVersion; bool _vehicleCapabilitiesKnown; uint64_t _capabilityBits; + bool _highLatencyLink; QGCCameraManager* _cameras; @@ -966,6 +1016,7 @@ private: int _mavCommandRetryCount; static const int _mavCommandMaxRetryCount = 3; static const int _mavCommandAckTimeoutMSecs = 3000; + static const int _mavCommandAckTimeoutMSecsHighLatency = 120000; QString _prearmError; QTimer _prearmErrorTimer; @@ -1070,6 +1121,7 @@ private: VehicleWindFactGroup _windFactGroup; VehicleVibrationFactGroup _vibrationFactGroup; VehicleTemperatureFactGroup _temperatureFactGroup; + VehicleClockFactGroup _clockFactGroup; static const char* _rollFactName; static const char* _pitchFactName; @@ -1089,6 +1141,7 @@ private: static const char* _windFactGroupName; static const char* _vibrationFactGroupName; static const char* _temperatureFactGroupName; + static const char* _clockFactGroupName; static const int _vehicleUIUpdateRateMSecs = 100; diff --git a/src/VehicleSetup/FirmwareUpgrade.qml b/src/VehicleSetup/FirmwareUpgrade.qml index 9ae02382fa1b789d24c32e981b2792771d74c138..646bfcce3218d25381242ea58b7e8234fb56ab27 100644 --- a/src/VehicleSetup/FirmwareUpgrade.qml +++ b/src/VehicleSetup/FirmwareUpgrade.qml @@ -45,6 +45,7 @@ SetupPage { readonly property string highlightPrefix: "" readonly property string highlightSuffix: "" readonly property string welcomeText: qsTr("%1 can upgrade the firmware on Pixhawk devices, SiK Radios and PX4 Flow Smart Cameras.").arg(QGroundControl.appName) + readonly property string welcomeTextSingle: qsTr("Update the autopilot firmware to the latest version") readonly property string plugInText: "" + highlightPrefix + "Plug in your device" + highlightSuffix + " via USB to " + highlightPrefix + "start" + highlightSuffix + " firmware upgrade." readonly property string flashFailText: "If upgrade failed, make sure to connect " + highlightPrefix + "directly" + highlightSuffix + " to a powered USB port on your computer, not through a USB hub. " + "Also make sure you are only powered via USB " + highlightPrefix + "not battery" + highlightSuffix + "." @@ -55,7 +56,7 @@ SetupPage { readonly property int _defaultFimwareTypeAPM: 3 property var _defaultFirmwareFact: QGroundControl.settingsManager.appSettings.defaultFirmwareType - property bool _defaultFirmwareIsPX4: _defaultFirmwareFact.rawValue == _defaultFimwareTypePX4 + property bool _defaultFirmwareIsPX4: _defaultFirmwareFact.rawValue === _defaultFimwareTypePX4 property string firmwareWarningMessage property bool controllerCompleted: false @@ -310,7 +311,7 @@ SetupPage { visible: !px4Flow Rectangle { - height: 1 + height: 1 width: ScreenTools.defaultFontPixelWidth * 5 color: qgcPal.text anchors.verticalCenter: _advanced.verticalCenter @@ -340,7 +341,7 @@ SetupPage { width: parent.width wrapMode: Text.WordWrap visible: showFirmwareTypeSelection - text: px4Flow ? qsTr("Select which version of the firmware you would like to install:") : qsTr("Select which version of the above flight stack you would like to install:") + text: _singleFirmwareMode ? qsTr("Select the standard version or one from the file system (previously downloaded):") : (px4Flow ? qsTr("Select which version of the firmware you would like to install:") : qsTr("Select which version of the above flight stack you would like to install:")) } QGCComboBox { @@ -353,13 +354,13 @@ SetupPage { onActivated: { controller.selectedFirmwareType = index - if (model.get(index).firmwareType == FirmwareUpgradeController.BetaFirmware) { + if (model.get(index).firmwareType === FirmwareUpgradeController.BetaFirmware) { firmwareVersionWarningLabel.visible = true firmwareVersionWarningLabel.text = qsTr("WARNING: BETA FIRMWARE. ") + qsTr("This firmware version is ONLY intended for beta testers. ") + qsTr("Although it has received FLIGHT TESTING, it represents actively changed code. ") + qsTr("Do NOT use for normal operation.") - } else if (model.get(index).firmwareType == FirmwareUpgradeController.DeveloperFirmware) { + } else if (model.get(index).firmwareType === FirmwareUpgradeController.DeveloperFirmware) { firmwareVersionWarningLabel.visible = true firmwareVersionWarningLabel.text = qsTr("WARNING: CONTINUOUS BUILD FIRMWARE. ") + qsTr("This firmware has NOT BEEN FLIGHT TESTED. ") + @@ -410,7 +411,7 @@ SetupPage { frameVisible: false font.pointSize: ScreenTools.defaultFontPointSize textFormat: TextEdit.RichText - text: welcomeText + text: _singleFirmwareMode ? welcomeTextSingle : welcomeText style: TextAreaStyle { textColor: qgcPal.text diff --git a/src/VehicleSetup/FirmwareUpgradeController.cc b/src/VehicleSetup/FirmwareUpgradeController.cc index cbf9336f7e39691d1e6f35590d96fe2a3eda13f4..f417e86ab20abd946667b818eba8579507a818df 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.cc +++ b/src/VehicleSetup/FirmwareUpgradeController.cc @@ -301,24 +301,6 @@ void FirmwareUpgradeController::_initFirmwareHash() { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://gumstix-aerocore.s3.amazonaws.com/Rover/latest/PX4/APMrover2-v2.px4"} }; - /////////////////////////////// FMUV1 firmwares /////////////////////////////////////////// - FirmwareToUrlElement_t rgPX4FMUV1FirmwareArray[] = { - { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/px4fmu-v1_default.px4"}, - { AutoPilotStackPX4, BetaFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/beta/px4fmu-v1_default.px4"}, - { AutoPilotStackPX4, DeveloperFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/master/px4fmu-v1_default.px4"}, - { AutoPilotStackAPM, StableFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, StableFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/stable/PX4-heli/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, StableFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/stable/PX4/ArduPlane-v1.px4"}, - { AutoPilotStackAPM, StableFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/stable/PX4/APMrover2-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/beta/PX4-heli/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/beta/PX4/ArduPlane-v1.px4"}, - { AutoPilotStackAPM, BetaFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/beta/PX4/APMrover2-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, CopterFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, HeliFirmware, "http://firmware.ardupilot.org/Copter/latest/PX4-heli/ArduCopter-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, PlaneFirmware, "http://firmware.ardupilot.org/Plane/latest/PX4/ArduPlane-v1.px4"}, - { AutoPilotStackAPM, DeveloperFirmware, RoverFirmware, "http://firmware.ardupilot.org/Rover/latest/PX4/APMrover2-v1.px4"} - }; //////////////////////////////////// AUAVX2_1 firmwares ////////////////////////////////////////////////// FirmwareToUrlElement_t rgAUAVX2_1FirmwareArray[] = { { AutoPilotStackPX4, StableFirmware, DefaultVehicleFirmware, "http://px4-travis.s3.amazonaws.com/Firmware/stable/auav-x21_default.px4"}, @@ -414,12 +396,6 @@ void FirmwareUpgradeController::_initFirmwareHash() _rgAeroCoreFirmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); } - size = sizeof(rgPX4FMUV1FirmwareArray)/sizeof(rgPX4FMUV1FirmwareArray[0]); - for (int i = 0; i < size; i++) { - const FirmwareToUrlElement_t& element = rgPX4FMUV1FirmwareArray[i]; - _rgPX4FMUV1Firmware.insert(FirmwareIdentifier(element.stackType, element.firmwareType, element.vehicleType), element.url); - } - size = sizeof(rgAUAVX2_1FirmwareArray)/sizeof(rgAUAVX2_1FirmwareArray[0]); for (int i = 0; i < size; i++) { const FirmwareToUrlElement_t& element = rgAUAVX2_1FirmwareArray[i]; @@ -479,8 +455,6 @@ void FirmwareUpgradeController::_bootloaderSyncFailed(void) QHash* FirmwareUpgradeController::_firmwareHashForBoardId(int boardId) { switch (boardId) { - case Bootloader::boardIDPX4FMUV1: - return &_rgPX4FMUV1Firmware; case Bootloader::boardIDPX4Flow: return &_rgPX4FLowFirmware; case Bootloader::boardIDPX4FMUV2: diff --git a/src/VehicleSetup/FirmwareUpgradeController.h b/src/VehicleSetup/FirmwareUpgradeController.h index 139727f16a1f43d78e19ec096a56caf3f31a7996..80b1d563208543e6a6a4d1820a8defb42e11aaa2 100644 --- a/src/VehicleSetup/FirmwareUpgradeController.h +++ b/src/VehicleSetup/FirmwareUpgradeController.h @@ -206,7 +206,6 @@ private: QHash _rgPX4FMUV3Firmware; QHash _rgPX4FMUV2Firmware; QHash _rgAeroCoreFirmware; - QHash _rgPX4FMUV1Firmware; QHash _rgAUAVX2_1Firmware; QHash _rgMindPXFMUV2Firmware; QHash _rgTAPV1Firmware; diff --git a/src/VehicleSetup/JoystickConfig.qml b/src/VehicleSetup/JoystickConfig.qml index 7efe052d59ec5ced75b4a5ae1703669f054b7d1a..df9a8d22c6264ea186f9275efda8f3c41b012f2e 100644 --- a/src/VehicleSetup/JoystickConfig.qml +++ b/src/VehicleSetup/JoystickConfig.qml @@ -85,6 +85,8 @@ SetupPage { Item { property int axisValue: 0 property int deadbandValue: 0 + property bool narrowIndicator: false + property color deadbandColor: "#8c161a" property color __barColor: qgcPal.windowShade @@ -104,7 +106,7 @@ SetupPage { x: _deadbandPosition width: _deadbandWidth height: parent.height / 2 - color: "#8c161a" + color: deadbandColor visible: controller.deadbandToggle property real _percentDeadband: ((2 * deadbandValue) / (32768.0 * 2)) @@ -123,8 +125,8 @@ SetupPage { // Indicator Rectangle { anchors.verticalCenter: parent.verticalCenter - width: parent.height * 0.75 - height: width + width: parent.narrowIndicator ? height/6 : height + height: parent.height * 0.75 x: (reversed ? (parent.width - _indicatorPosition) : _indicatorPosition) - (width / 2) radius: width / 2 color: qgcPal.text @@ -364,9 +366,15 @@ SetupPage { id: enabledCheckBox enabled: _activeJoystick ? _activeJoystick.calibrated : false text: _activeJoystick ? _activeJoystick.calibrated ? qsTr("Enable joystick input") : qsTr("Enable not allowed (Calibrate First)") : "" - checked: _activeVehicle.joystickEnabled - onClicked: _activeVehicle.joystickEnabled = checked + onClicked: _activeJoystick.outputEnabled = checked + + Connections { + target: _activeJoystick + onOutputEnabledChanged: { + enabledCheckBox.checked=enabled + } + } Connections { target: joystickManager @@ -535,6 +543,21 @@ SetupPage { onClicked: controller.deadbandToggle = checked } } + Row{ + width: parent.width + spacing: ScreenTools.defaultFontPixelWidth + visible: advancedSettings.checked + QGCLabel{ + width: parent.width * 0.85 + font.pointSize: ScreenTools.smallFontPointSize + wrapMode: Text.WordWrap + text: qsTr("Deadband can be set during the first ") + + qsTr("step of calibration by gently wiggling each axis. ") + + qsTr("Deadband can also be adjusted by clicking and ") + + qsTr("dragging vertically on the corresponding axis monitor.") + visible: controller.deadbandToggle + } + } } } // Column - left column @@ -790,11 +813,36 @@ SetupPage { height: ScreenTools.defaultFontPixelHeight width: 200 sourceComponent: axisMonitorDisplayComponent + Component.onCompleted: item.narrowIndicator = true property real defaultTextWidth: ScreenTools.defaultFontPixelWidth property bool mapped: true readonly property bool reversed: false + + + MouseArea { + id: deadbandMouseArea + anchors.fill: parent.item + enabled: controller.deadbandToggle + + property real startY + + onPressed: { + startY = mouseY + parent.item.deadbandColor = "#3C6315" + } + onPositionChanged: { + var newValue = parent.item.deadbandValue + (startY - mouseY)*15 + if ((newValue > 0) && (newValue <32768)){parent.item.deadbandValue=newValue;} + startY = mouseY + } + onReleased: { + controller.setDeadbandValue(modelData,parent.item.deadbandValue) + parent.item.deadbandColor = "#8c161a" + } + } } + } } } // Column - Axis Monitor diff --git a/src/VehicleSetup/JoystickConfigController.cc b/src/VehicleSetup/JoystickConfigController.cc index 6168ce338f5052136ad9fc2fe9014fc7daa0846b..3ee4119eba7a27fe2631e3694ae3cbb914dcfe1e 100644 --- a/src/VehicleSetup/JoystickConfigController.cc +++ b/src/VehicleSetup/JoystickConfigController.cc @@ -16,7 +16,6 @@ QGC_LOGGING_CATEGORY(JoystickConfigControllerLog, "JoystickConfigControllerLog") -const int JoystickConfigController::_updateInterval = 150; ///< Interval for timer which updates radio channel widgets const int JoystickConfigController::_calCenterPoint = 0; const int JoystickConfigController::_calValidMinValue = -32768; ///< Largest valid minimum axis value const int JoystickConfigController::_calValidMaxValue = 32767; ///< Smallest valid maximum axis value @@ -71,10 +70,19 @@ void JoystickConfigController::start(void) _stopCalibration(); } +void JoystickConfigController::setDeadbandValue(int axis, int value) +{ + _axisDeadbandChanged(axis,value); + Joystick* joystick = _joystickManager->activeJoystick(); + Joystick::Calibration_t calibration = joystick->getCalibration(axis); + calibration.deadband = value; + joystick->setCalibration(axis,calibration); +} + JoystickConfigController::~JoystickConfigController() { if(_activeJoystick) { - _activeJoystick->stopCalibrationMode(Joystick::CalibrationModeMonitor); + _activeJoystick->setCalibrationMode(false); } } @@ -407,8 +415,8 @@ void JoystickConfigController::_inputCenterWait(Joystick::AxisFunction_t functio if (_stickDetectAxis == _axisNoAxis) { // Sticks have not yet moved close enough to center - - if (abs(_calCenterPoint - value) < _calRoughCenterDelta) { + int roughCenter = getDeadbandToggle() ? std::max(_rgAxisInfo[axis].deadband,_calRoughCenterDelta) : _calRoughCenterDelta; + if (abs(_calCenterPoint - value) < roughCenter) { // Stick has moved close enough to center that we can start waiting for it to settle _stickDetectAxis = axis; _stickDetectInitialValue = value; @@ -574,7 +582,7 @@ void JoystickConfigController::_writeCalibration(void) /// @brief Starts the calibration process void JoystickConfigController::_startCalibration(void) { - _activeJoystick->startCalibrationMode(Joystick::CalibrationModeCalibrating); + _activeJoystick->setCalibrationMode(true); _resetInternalCalibrationValues(); _nextButton->setProperty("text", "Next"); @@ -590,7 +598,7 @@ void JoystickConfigController::_stopCalibration(void) { _currentStep = -1; - _activeJoystick->stopCalibrationMode(Joystick::CalibrationModeCalibrating); + _activeJoystick->setCalibrationMode(false); _setInternalCalibrationValuesFromSettings(); _statusText->setProperty("text", ""); @@ -755,7 +763,6 @@ void JoystickConfigController::_signalAllAttitudeValueChanges(void) void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) { bool joystickTransition = false; - if (_activeJoystick) { joystickTransition = true; disconnect(_activeJoystick, &Joystick::rawAxisValueChanged, this, &JoystickConfigController::_axisValueChanged); @@ -773,7 +780,7 @@ void JoystickConfigController::_activeJoystickChanged(Joystick* joystick) if (joystickTransition) { _stopCalibration(); } - _activeJoystick->startCalibrationMode(Joystick::CalibrationModeMonitor); + _activeJoystick->setCalibrationMode(false); _axisCount = _activeJoystick->axisCount(); _rgAxisInfo = new struct AxisInfo[_axisCount]; _axisValueSave = new int[_axisCount]; diff --git a/src/VehicleSetup/JoystickConfigController.h b/src/VehicleSetup/JoystickConfigController.h index d0e1faf231b5324ca8cb6eac1b024b3010f6cb4b..a947e43b924a40a392a4011b733e573c941b5593 100644 --- a/src/VehicleSetup/JoystickConfigController.h +++ b/src/VehicleSetup/JoystickConfigController.h @@ -67,6 +67,7 @@ public: Q_INVOKABLE void skipButtonClicked(void); Q_INVOKABLE void nextButtonClicked(void); Q_INVOKABLE void start(void); + Q_INVOKABLE void setDeadbandValue(int axis, int value); bool rollAxisMapped(void); bool pitchAxisMapped(void); @@ -207,8 +208,6 @@ private: static const char* _imagePitchUp; static const char* _imagePitchDown; - static const int _updateInterval; ///< Interval for ui update timer - int _rgFunctionAxisMapping[Joystick::maxFunction]; ///< Maps from joystick function to axis index. _axisMax indicates axis not set for this function. static const int _attitudeControls = 5; diff --git a/src/VehicleSetup/SetupView.qml b/src/VehicleSetup/SetupView.qml index a1bdf64547a49d8b3a7566d00764790cfba3a1d5..9b94e26c72a696d83d3ecf87b62fc3b82a605b73 100644 --- a/src/VehicleSetup/SetupView.qml +++ b/src/VehicleSetup/SetupView.qml @@ -34,6 +34,7 @@ Rectangle { readonly property real _buttonWidth: _defaultTextWidth * 18 readonly property string _armedVehicleText: qsTr("This operation cannot be performed while the vehicle is armed.") + property bool _vehicleArmed: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.armed : false property string _messagePanelText: qsTr("missing message panel text") property bool _fullParameterVehicleAvailable: QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable && !QGroundControl.multiVehicleManager.activeVehicle.parameterManager.missingParameters property var _corePlugin: QGroundControl.corePlugin @@ -41,7 +42,7 @@ Rectangle { function showSummaryPanel() { if (_fullParameterVehicleAvailable) { - if (QGroundControl.multiVehicleManager.activeVehicle.autopilot.vehicleComponents.length == 0) { + if (QGroundControl.multiVehicleManager.activeVehicle.autopilot.vehicleComponents.length === 0) { panelLoader.setSourceComponent(noComponentsVehicleSummaryComponent) } else { panelLoader.setSource("VehicleSummary.qml") @@ -80,8 +81,7 @@ Rectangle { var autopilotPlugin = QGroundControl.multiVehicleManager.activeVehicle.autopilot var prereq = autopilotPlugin.prerequisiteSetup(vehicleComponent) if (prereq !== "") { - //-- TODO: This cannot be translated when built this way. - _messagePanelText = prereq + " setup must be completed prior to " + vehicleComponent.name + " setup." + _messagePanelText = qsTr("%1 setup must be completed prior to %2 setup.").arg(prereq).arg(vehicleComponent.name) panelLoader.setSourceComponent(messagePanelComponent) } else { panelLoader.setSource(vehicleComponent.setupSource, vehicleComponent) @@ -272,7 +272,7 @@ Rectangle { setupIndicator: true setupComplete: joystickManager.activeJoystick ? joystickManager.activeJoystick.calibrated : false exclusiveGroup: setupButtonGroup - visible: _fullParameterVehicleAvailable && joystickManager.joysticks.length != 0 + visible: _fullParameterVehicleAvailable && joystickManager.joysticks.length !== 0 text: qsTr("Joystick") Layout.fillWidth: true @@ -289,7 +289,7 @@ Rectangle { setupComplete: modelData.setupComplete exclusiveGroup: setupButtonGroup text: modelData.name - visible: modelData.setupSource.toString() != "" + visible: modelData.setupSource.toString() !== "" Layout.fillWidth: true onClicked: showVehicleComponentPanel(modelData) @@ -299,7 +299,9 @@ Rectangle { SubMenuButton { setupIndicator: false exclusiveGroup: setupButtonGroup - visible: QGroundControl.multiVehicleManager && QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable && _corePlugin.showAdvancedUI + visible: QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable && + !QGroundControl.multiVehicleManager.activeVehicle.highLatencyLink && + _corePlugin.showAdvancedUI text: qsTr("Parameters") Layout.fillWidth: true diff --git a/src/VehicleSetup/VehicleComponent.h b/src/VehicleSetup/VehicleComponent.h index 28eb585ed78c92a8556dc145587ae26ce406b037..f452467ab7955e319c2aabb4779ad496bbc65914 100644 --- a/src/VehicleSetup/VehicleComponent.h +++ b/src/VehicleSetup/VehicleComponent.h @@ -56,7 +56,7 @@ public: virtual void addSummaryQmlComponent(QQmlContext* context, QQuickItem* parent); /// @brief Returns an list of parameter names for which a change should cause the setupCompleteChanged - /// signal to be emitted. Last element is signalled by NULL. + /// signal to be emitted. virtual QStringList setupCompleteChangedTriggerList(void) const = 0; /// Should be called after the component is created (but not in constructor) to setup the diff --git a/src/VehicleSetup/VehicleSummary.qml b/src/VehicleSetup/VehicleSummary.qml index 9bfb18b52ec0184b893b0c3512b66c22a8b8c248..60286d44d063d3ec50cf9030b7a23a83ef755e3b 100644 --- a/src/VehicleSetup/VehicleSummary.qml +++ b/src/VehicleSetup/VehicleSummary.qml @@ -86,6 +86,7 @@ Rectangle { text: setupComplete ? qsTr("Below you will find a summary of the settings for your vehicle. To the left are the setup menus for each component.") : qsTr("WARNING: Your vehicle requires setup prior to flight. Please resolve the items marked in red using the menu on the left.") + property bool setupComplete: QGroundControl.multiVehicleManager.activeVehicle ? QGroundControl.multiVehicleManager.activeVehicle.autopilot.setupComplete : false } @@ -127,12 +128,12 @@ Rectangle { height: width radius: width / 2 color: modelData.setupComplete ? "#00d932" : "red" - visible: modelData.requiresSetup && modelData.setupSource != "" + visible: modelData.requiresSetup && modelData.setupSource !== "" } onClicked : { console.log(modelData.setupSource) - if (modelData.setupSource != "") { + if (modelData.setupSource !== "") { setupView.showVehicleComponentPanel(modelData) } } diff --git a/src/VideoStreaming/README.md b/src/VideoStreaming/README.md index f1753d3d11dbbc50e6d274171c9302020b50ab98..585cb9cf8805fa004d62073531028acf94961d8f 100644 --- a/src/VideoStreaming/README.md +++ b/src/VideoStreaming/README.md @@ -35,7 +35,10 @@ gst-launch-1.0 udpsrc port=5600 caps='application/x-rtp, media=(string)video, cl Use apt-get to install GStreamer 1.0 ``` -sudo apt-get install gstreamer1.0* +list=$(apt-cache --names-only search ^gstreamer1.0-* | awk '{ print $1 }' | grep -v gstreamer1.0-hybris) +``` +``` +sudo apt-get install $list ``` The build system is setup to use pkgconfig and it will find the necessary headers and libraries automatically. diff --git a/src/VideoStreaming/VideoReceiver.cc b/src/VideoStreaming/VideoReceiver.cc index 5091f215a1bc134c1f65ea02c50dcced9ae1143a..7f46d85650258331bfd533f0c89ed9d4e2d68e2c 100644 --- a/src/VideoStreaming/VideoReceiver.cc +++ b/src/VideoStreaming/VideoReceiver.cc @@ -67,8 +67,10 @@ VideoReceiver::VideoReceiver(QObject* parent) , _videoSurface(NULL) , _videoRunning(false) , _showFullScreen(false) + , _videoSettings(NULL) { - _videoSurface = new VideoSurface; + _videoSurface = new VideoSurface; + _videoSettings = qgcApp()->toolbox()->settingsManager()->videoSettings(); #if defined(QGC_GST_STREAMING) _setVideoSink(_videoSurface->videoSink()); _timer.setSingleShot(true); @@ -147,8 +149,10 @@ VideoReceiver::_connected() _timer.stop(); _socket->deleteLater(); _socket = NULL; - _serverPresent = true; - start(); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + _serverPresent = true; + start(); + } } #endif @@ -161,7 +165,9 @@ VideoReceiver::_socketError(QAbstractSocket::SocketError socketError) _socket->deleteLater(); _socket = NULL; //-- Try again in 5 seconds - _timer.start(5000); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + _timer.start(5000); + } } #endif @@ -175,18 +181,21 @@ VideoReceiver::_timeout() delete _socket; _socket = NULL; } - //-- RTSP will try to connect to the server. If it cannot connect, - // it will simply give up and never try again. Instead, we keep - // attempting a connection on this timer. Once a connection is - // found to be working, only then we actually start the stream. - QUrl url(_uri); - _socket = new QTcpSocket; - _socket->setProxy(QNetworkProxy::NoProxy); - connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); - connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); - //qCDebug(VideoReceiverLog) << "Trying to connect to:" << url.host() << url.port(); - _socket->connectToHost(url.host(), url.port()); - _timer.start(5000); + if(_videoSettings->streamEnabled()->rawValue().toBool()) { + //-- RTSP will try to connect to the server. If it cannot connect, + // it will simply give up and never try again. Instead, we keep + // attempting a connection on this timer. Once a connection is + // found to be working, only then we actually start the stream. + QUrl url(_uri); + _socket = new QTcpSocket; + QNetworkProxy tempProxy; + tempProxy.setType(QNetworkProxy::DefaultProxy); + _socket->setProxy(tempProxy); + connect(_socket, static_cast(&QTcpSocket::error), this, &VideoReceiver::_socketError); + connect(_socket, &QTcpSocket::connected, this, &VideoReceiver::_connected); + _socket->connectToHost(url.host(), url.port()); + _timer.start(5000); + } } #endif @@ -203,6 +212,11 @@ VideoReceiver::_timeout() void VideoReceiver::start() { + if(!_videoSettings->streamEnabled()->rawValue().toBool() || + !_videoSettings->streamConfigured()) { + qCDebug(VideoReceiverLog) << "start() but not enabled/configured"; + return; + } #if defined(QGC_GST_STREAMING) qCDebug(VideoReceiverLog) << "start()"; @@ -352,6 +366,7 @@ VideoReceiver::start() bus = NULL; } + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-paused"); running = gst_element_set_state(_pipeline, GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE; } while(0); @@ -405,6 +420,7 @@ VideoReceiver::start() _running = false; } else { + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-playing"); _running = true; qCDebug(VideoReceiverLog) << "Running"; } @@ -550,7 +566,7 @@ void VideoReceiver::_cleanupOldVideos() { //-- Only perform cleanup if storage limit is enabled - if(qgcApp()->toolbox()->settingsManager()->videoSettings()->enableStorageLimit()->rawValue().toBool()) { + if(_videoSettings->enableStorageLimit()->rawValue().toBool()) { QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath(); QDir videoDir = QDir(savePath); videoDir.setFilter(QDir::Files | QDir::Readable | QDir::NoSymLinks | QDir::Writable); @@ -566,7 +582,7 @@ VideoReceiver::_cleanupOldVideos() if(!vidList.isEmpty()) { uint64_t total = 0; //-- Settings are stored using MB - uint64_t maxSize = (qgcApp()->toolbox()->settingsManager()->videoSettings()->maxVideoSize()->rawValue().toUInt() * 1024 * 1024); + uint64_t maxSize = (_videoSettings->maxVideoSize()->rawValue().toUInt() * 1024 * 1024); //-- Compute total used storage for(int i = 0; i < vidList.size(); i++) { total += vidList[i].size(); @@ -597,7 +613,7 @@ VideoReceiver::_cleanupOldVideos() // | | // +--------------------------------------+ void -VideoReceiver::startRecording(void) +VideoReceiver::startRecording(const QString &videoFile) { #if defined(QGC_GST_STREAMING) @@ -608,13 +624,7 @@ VideoReceiver::startRecording(void) return; } - QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath(); - if(savePath.isEmpty()) { - qgcApp()->showMessage(tr("Unabled to record video. Video save path must be specified in Settings.")); - return; - } - - uint32_t muxIdx = qgcApp()->toolbox()->settingsManager()->videoSettings()->recordingFormat()->rawValue().toUInt(); + uint32_t muxIdx = _videoSettings->recordingFormat()->rawValue().toUInt(); if(muxIdx >= NUM_MUXES) { qgcApp()->showMessage(tr("Invalid video format defined.")); return; @@ -636,11 +646,20 @@ VideoReceiver::startRecording(void) return; } - QString videoFile; - videoFile = savePath + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") + "." + kVideoExtensions[muxIdx]; + if(videoFile.isEmpty()) { + QString savePath = qgcApp()->toolbox()->settingsManager()->appSettings()->videoSavePath(); + if(savePath.isEmpty()) { + qgcApp()->showMessage(tr("Unabled to record video. Video save path must be specified in Settings.")); + return; + } + _videoFile = savePath + "/" + QDateTime::currentDateTime().toString("yyyy-MM-dd_hh.mm.ss") + "." + kVideoExtensions[muxIdx]; + } else { + _videoFile = videoFile; + } + emit videoFileChanged(); - g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(videoFile), NULL); - qCDebug(VideoReceiverLog) << "New video file:" << videoFile; + g_object_set(G_OBJECT(_sink->filesink), "location", qPrintable(_videoFile), NULL); + qCDebug(VideoReceiverLog) << "New video file:" << _videoFile; gst_object_ref(_sink->queue); gst_object_ref(_sink->parse); @@ -659,9 +678,13 @@ VideoReceiver::startRecording(void) gst_pad_link(_sink->teepad, sinkpad); gst_object_unref(sinkpad); + GST_DEBUG_BIN_TO_DOT_FILE(GST_BIN(_pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "pipeline-recording"); + _recording = true; emit recordingChanged(); qCDebug(VideoReceiverLog) << "Recording started"; +#else + Q_UNUSED(videoFile) #endif } @@ -803,7 +826,7 @@ VideoReceiver::_updateTimer() if(_videoRunning) { uint32_t timeout = 1; if(qgcApp()->toolbox() && qgcApp()->toolbox()->settingsManager()) { - timeout = qgcApp()->toolbox()->settingsManager()->videoSettings()->rtspTimeout()->rawValue().toUInt(); + timeout = _videoSettings->rtspTimeout()->rawValue().toUInt(); } time_t elapsed = 0; time_t lastFrame = _videoSurface->lastFrame(); @@ -814,7 +837,7 @@ VideoReceiver::_updateTimer() stop(); } } else { - if(!running() && !_uri.isEmpty()) { + if(!running() && !_uri.isEmpty() && _videoSettings->streamEnabled()->rawValue().toBool()) { start(); } } diff --git a/src/VideoStreaming/VideoReceiver.h b/src/VideoStreaming/VideoReceiver.h index 7e7cfaf5601d61500e06ac8fc1aaf1b137c24404..843abdf144ea6b0d3e18529ea9abfbd06df653cb 100644 --- a/src/VideoStreaming/VideoReceiver.h +++ b/src/VideoStreaming/VideoReceiver.h @@ -30,6 +30,8 @@ Q_DECLARE_LOGGING_CATEGORY(VideoReceiverLog) +class VideoSettings; + class VideoReceiver : public QObject { Q_OBJECT @@ -38,9 +40,10 @@ public: Q_PROPERTY(bool recording READ recording NOTIFY recordingChanged) #endif Q_PROPERTY(VideoSurface* videoSurface READ videoSurface CONSTANT) - Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) - Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged) - Q_PROPERTY(bool showFullScreen READ showFullScreen WRITE setShowFullScreen NOTIFY showFullScreenChanged) + Q_PROPERTY(bool videoRunning READ videoRunning NOTIFY videoRunningChanged) + Q_PROPERTY(QString imageFile READ imageFile NOTIFY imageFileChanged) + Q_PROPERTY(QString videoFile READ videoFile NOTIFY videoFileChanged) + Q_PROPERTY(bool showFullScreen READ showFullScreen WRITE setShowFullScreen NOTIFY showFullScreenChanged) explicit VideoReceiver(QObject* parent = 0); ~VideoReceiver(); @@ -56,7 +59,9 @@ public: VideoSurface* videoSurface () { return _videoSurface; } bool videoRunning () { return _videoRunning; } QString imageFile () { return _imageFile; } + QString videoFile () { return _videoFile; } bool showFullScreen () { return _showFullScreen; } + void grabImage (QString imageFile); void setShowFullScreen (bool show) { _showFullScreen = show; emit showFullScreenChanged(); } @@ -64,6 +69,7 @@ public: signals: void videoRunningChanged (); void imageFileChanged (); + void videoFileChanged (); void showFullScreenChanged (); #if defined(QGC_GST_STREAMING) void recordingChanged (); @@ -77,7 +83,7 @@ public slots: void stop (); void setUri (const QString& uri); void stopRecording (); - void startRecording (); + void startRecording (const QString& videoFile = QString()); private slots: void _updateTimer (); @@ -133,10 +139,11 @@ private: QString _uri; QString _imageFile; + QString _videoFile; VideoSurface* _videoSurface; bool _videoRunning; bool _showFullScreen; - + VideoSettings* _videoSettings; }; #endif // VIDEORECEIVER_H diff --git a/src/VideoStreaming/VideoStreaming.cc b/src/VideoStreaming/VideoStreaming.cc index 57579c3e97bfc171f51c265e7b766c03a825af48..d7ec9a912bdc005d34f3e4e7cc7ed7efad88be98 100644 --- a/src/VideoStreaming/VideoStreaming.cc +++ b/src/VideoStreaming/VideoStreaming.cc @@ -101,7 +101,7 @@ int start_logger(const char *app_name) } #endif -void initializeVideoStreaming(int &argc, char* argv[]) +void initializeVideoStreaming(int &argc, char* argv[], char* logpath, char* debuglevel) { #if defined(QGC_GST_STREAMING) #ifdef __macos__ @@ -119,12 +119,19 @@ void initializeVideoStreaming(int &argc, char* argv[]) QString currentDir = QCoreApplication::applicationDirPath(); qgcputenv("GST_PLUGIN_PATH", currentDir, "/gstreamer-plugins"); #endif + + // Initialize GStreamer - #ifdef ANDDROID_GST_DEBUG - start_logger("gst_log"); - qputenv("GST_DEBUG", "*:4"); - qputenv("GST_DEBUG_NO_COLOR", "1"); - #endif + if (logpath) { + if (debuglevel) { + qputenv("GST_DEBUG", debuglevel); + } + qputenv("GST_DEBUG_NO_COLOR", "1"); + qputenv("GST_DEBUG_FILE", QString("%1/%2").arg(logpath).arg("gstreamer-log.txt").toUtf8()); + qputenv("GST_DEBUG_DUMP_DOT_DIR", logpath); + } + + GError* error = NULL; if (!gst_init_check(&argc, &argv, &error)) { qCritical() << "gst_init_check() failed: " << error->message; @@ -148,6 +155,8 @@ void initializeVideoStreaming(int &argc, char* argv[]) #else Q_UNUSED(argc); Q_UNUSED(argv); + Q_UNUSED(logpath); + Q_UNUSED(debuglevel); #endif qmlRegisterType ("QGroundControl.QgcQtGStreamer", 1, 0, "VideoItem"); qmlRegisterUncreatableType("QGroundControl.QgcQtGStreamer", 1, 0, "VideoSurface", QLatin1String("VideoSurface from QML is not supported")); diff --git a/src/VideoStreaming/VideoStreaming.h b/src/VideoStreaming/VideoStreaming.h index 74b4281b5d33970bd3a5510e2f0311746850eee0..f918f6ed86af6b8a18192b7537187471fdc7d651 100644 --- a/src/VideoStreaming/VideoStreaming.h +++ b/src/VideoStreaming/VideoStreaming.h @@ -17,7 +17,7 @@ #ifndef VIDEO_STREAMING_H #define VIDEO_STREAMING_H -extern void initializeVideoStreaming (int &argc, char *argv[]); +extern void initializeVideoStreaming (int &argc, char *argv[], char* filename, char* debuglevel); extern void shutdownVideoStreaming (); #endif // VIDEO_STREAMING_H diff --git a/src/ViewWidgets/CustomCommandWidget.qml b/src/ViewWidgets/CustomCommandWidget.qml index c73ed1635d7dd5941018be2d1d9bbdb4dab0e1af..e937aef5ee105664e42899e091a72c9a8ce64c83 100644 --- a/src/ViewWidgets/CustomCommandWidget.qml +++ b/src/ViewWidgets/CustomCommandWidget.qml @@ -7,25 +7,25 @@ * ****************************************************************************/ - -/// @file -/// @author Don Gagne - import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 import QtQuick.Dialogs 1.2 +import QGroundControl 1.0 import QGroundControl.Palette 1.0 import QGroundControl.Controls 1.0 import QGroundControl.Controllers 1.0 import QGroundControl.ScreenTools 1.0 QGCView { + id: qgcView viewPanel: panel - property real _margins: ScreenTools.defaultFontPixelHeight - property string _emptyText: "

" + + property var _activeVehicle: QGroundControl.multiVehicleManager.parameterReadyVehicleAvailable ? QGroundControl.multiVehicleManager.activeVehicle : null + property real _margins: ScreenTools.defaultFontPixelHeight + property string _noVehicleText: qsTr("No vehicle connected") + property string _assignQmlFile: "

" + "You can create your own commands and parameter editing user interface in this widget. " + "You do this by providing your own Qml file. " + "This support is a work in progress and the details may change somewhat in the future. " + @@ -34,29 +34,42 @@ QGCView { "So make sure to test your changes thoroughly before using them in flight.

" + "

Click 'Load Custom Qml file' to provide your custom qml file.

" + "

Click 'Reset' to reset to none.

" + - "

Example usage: http://www.qgroundcontrol.org/custom_command_qml_widgets

" + "

Example usage: http://https://dev.qgroundcontrol.com/en/tools/custom_command_widget.html

" + + QGCPalette { id: qgcPal; colorGroupEnabled: enabled } + + CustomCommandWidgetController { + id: controller + factPanel: panel + + onCustomQmlFileChanged: _updateLoader() + } - QGCPalette { id: qgcPal; colorGroupEnabled: enabled } - CustomCommandWidgetController { id: controller; factPanel: panel } + Component.onCompleted: _updateLoader() + + on_ActiveVehicleChanged: _updateLoader() + + function _updateLoader() { + loader.sourceComponent = undefined + loader.visible = false + textOutput.text = _noVehicleText + if (_activeVehicle) { + if (controller.customQmlFile == "") { + textOutput.text = _assignQmlFile + } else { + loader.source = controller.customQmlFile + } + } + } QGCViewPanel { id: panel anchors.fill: parent + Rectangle { anchors.fill: parent color: qgcPal.window - QGCLabel { - id: textOutput - anchors.margins: _margins - anchors.left: parent.left - anchors.right: parent.right - anchors.top: parent.top - anchors.bottom: buttonRow.top - wrapMode: Text.WordWrap - textFormat: Text.RichText - text: _emptyText - visible: !loader.visible - } + Loader { id: loader anchors.margins: _margins @@ -64,37 +77,42 @@ QGCView { anchors.right: parent.right anchors.top: parent.top anchors.bottom: buttonRow.top - source: controller.customQmlFile visible: false + onStatusChanged: { - loader.visible = true if (loader.status == Loader.Error) { - if (sourceComponent.status == Component.Error) { - textOutput.text = sourceComponent.errorString() - loader.visible = false - } + textOutput.text = sourceComponent.errorString() + } else if (loader.status == Loader.Ready) { + loader.visible = true } } } + + QGCLabel { + id: textOutput + anchors.fill: loader + wrapMode: Text.WordWrap + textFormat: Text.RichText + visible: !loader.visible + } + Row { id: buttonRow spacing: ScreenTools.defaultFontPixelWidth anchors.margins: _margins anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter + QGCButton { text: qsTr("Load Custom Qml file...") width: ScreenTools.defaultFontPixelWidth * 22 onClicked: controller.selectQmlFile() } + QGCButton { text: qsTr("Reset") width: ScreenTools.defaultFontPixelWidth * 22 - onClicked: { - controller.clearQmlFile() - loader.visible = false - textOutput.text = _emptyText - } + onClicked: controller.clearQmlFile() } } } diff --git a/src/ViewWidgets/CustomCommandWidgetController.cc b/src/ViewWidgets/CustomCommandWidgetController.cc index 071a477b052635845e8e181394ba56d37bdbbdd6..70d329938d173f7d538c4687f752abb79ba87800 100644 --- a/src/ViewWidgets/CustomCommandWidgetController.cc +++ b/src/ViewWidgets/CustomCommandWidgetController.cc @@ -28,7 +28,6 @@ CustomCommandWidgetController::CustomCommandWidgetController(void) : } QSettings settings; _customQmlFile = settings.value(_settingsKey).toString(); - connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::activeVehicleChanged, this, &CustomCommandWidgetController::_activeVehicleChanged); } void CustomCommandWidgetController::sendCommand(int commandId, QVariant componentId, QVariant confirm, QVariant param1, QVariant param2, QVariant param3, QVariant param4, QVariant param5, QVariant param6, QVariant param7) @@ -43,13 +42,6 @@ void CustomCommandWidgetController::sendCommand(int commandId, QVariant componen } } -void CustomCommandWidgetController::_activeVehicleChanged(Vehicle* activeVehicle) -{ - if (activeVehicle) { - _vehicle = activeVehicle; - } -} - void CustomCommandWidgetController::selectQmlFile(void) { QSettings settings; diff --git a/src/ViewWidgets/CustomCommandWidgetController.h b/src/ViewWidgets/CustomCommandWidgetController.h index 514e7a69225c2290d06b250c82d11839a9c7813b..c507c70c155137878d2444b69e98f9c2dd2d3c0b 100644 --- a/src/ViewWidgets/CustomCommandWidgetController.h +++ b/src/ViewWidgets/CustomCommandWidgetController.h @@ -32,9 +32,6 @@ public: signals: void customQmlFileChanged (const QString& customQmlFile); -private slots: - void _activeVehicleChanged (Vehicle* activeVehicle); - private: Vehicle* _vehicle; QString _customQmlFile; diff --git a/src/ViewWidgets/ViewWidget.qml b/src/ViewWidgets/ViewWidget.qml index 04c0c2f14308a63ade1b35f850e717155a8c38a2..0eef437883e717a412cce3a44f84dd1ad2ffde01 100644 --- a/src/ViewWidgets/ViewWidget.qml +++ b/src/ViewWidgets/ViewWidget.qml @@ -2,8 +2,10 @@ import QtQuick 2.3 import QtQuick.Controls 1.2 import QtQuick.Controls.Styles 1.4 -import QGroundControl.Palette 1.0 -import QGroundControl.Controllers 1.0 +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.Controllers 1.0 +import QGroundControl.Palette 1.0 Rectangle { property Component connectedComponent: __componentConnected diff --git a/src/api/QGCCorePlugin.cc b/src/api/QGCCorePlugin.cc index de2d76d00ee6a22dde0e3298ee74223b646c46a2..58b06834e82948323797611d4de59f72d147f161 100644 --- a/src/api/QGCCorePlugin.cc +++ b/src/api/QGCCorePlugin.cc @@ -39,6 +39,7 @@ public: , defaultOptions (NULL) , valuesPageWidgetInfo (NULL) , cameraPageWidgetInfo (NULL) + , videoPageWidgetInfo (NULL) , healthPageWidgetInfo (NULL) , vibrationPageWidgetInfo (NULL) { @@ -80,6 +81,7 @@ public: QmlComponentInfo* valuesPageWidgetInfo; QmlComponentInfo* cameraPageWidgetInfo; + QmlComponentInfo* videoPageWidgetInfo; QmlComponentInfo* healthPageWidgetInfo; QmlComponentInfo* vibrationPageWidgetInfo; QVariantList instrumentPageWidgetList; @@ -148,13 +150,19 @@ QVariantList &QGCCorePlugin::settingsPages() QVariantList& QGCCorePlugin::instrumentPages(void) { if (!_p->valuesPageWidgetInfo) { - _p->valuesPageWidgetInfo = new QmlComponentInfo(tr("Values"), QUrl::fromUserInput("qrc:/qml/ValuePageWidget.qml")); - _p->cameraPageWidgetInfo = new QmlComponentInfo(tr("Camera"), QUrl::fromUserInput("qrc:/qml/CameraPageWidget.qml")); - _p->healthPageWidgetInfo = new QmlComponentInfo(tr("Health"), QUrl::fromUserInput("qrc:/qml/HealthPageWidget.qml")); + _p->valuesPageWidgetInfo = new QmlComponentInfo(tr("Values"), QUrl::fromUserInput("qrc:/qml/ValuePageWidget.qml")); + _p->cameraPageWidgetInfo = new QmlComponentInfo(tr("Camera"), QUrl::fromUserInput("qrc:/qml/CameraPageWidget.qml")); +#if defined(QGC_GST_STREAMING) + _p->videoPageWidgetInfo = new QmlComponentInfo(tr("Video Stream"), QUrl::fromUserInput("qrc:/qml/VideoPageWidget.qml")); +#endif + _p->healthPageWidgetInfo = new QmlComponentInfo(tr("Health"), QUrl::fromUserInput("qrc:/qml/HealthPageWidget.qml")); _p->vibrationPageWidgetInfo = new QmlComponentInfo(tr("Vibration"), QUrl::fromUserInput("qrc:/qml/VibrationPageWidget.qml")); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->valuesPageWidgetInfo)); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->cameraPageWidgetInfo)); +#if defined(QGC_GST_STREAMING) + _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->videoPageWidgetInfo)); +#endif _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->healthPageWidgetInfo)); _p->instrumentPageWidgetList.append(QVariant::fromValue(_p->vibrationPageWidgetInfo)); } diff --git a/src/api/QGCOptions.h b/src/api/QGCOptions.h index 2929cfbdf3f3f10fc292d37069a4ccda0c9dcac4..a441b0139c5e68d335075d07be7163308e16beb3 100644 --- a/src/api/QGCOptions.h +++ b/src/api/QGCOptions.h @@ -44,6 +44,8 @@ public: Q_PROPERTY(bool multiVehicleEnabled READ multiVehicleEnabled NOTIFY multiVehicleEnabledChanged) Q_PROPERTY(bool showOfflineMapExport READ showOfflineMapExport NOTIFY showOfflineMapExportChanged) Q_PROPERTY(bool showOfflineMapImport READ showOfflineMapImport NOTIFY showOfflineMapImportChanged) + Q_PROPERTY(bool useMobileFileDialog READ useMobileFileDialog CONSTANT) + Q_PROPERTY(bool showMissionStatus READ showMissionStatus CONSTANT) /// Should QGC hide its settings menu and colapse it into one single menu (Settings and Vehicle Setup)? /// @return true if QGC should consolidate both menus into one. @@ -61,6 +63,10 @@ public: /// @return An alternate widget (see QGCInstrumentWidget.qml, the default widget) virtual CustomInstrumentWidget* instrumentWidget(); + /// Should the mission status indicator (Plan View) be shown? + /// @return Yes or no + virtual bool showMissionStatus () { return true; } + /// Allows access to the full fly view window virtual QUrl flyViewOverlay () const { return QUrl(); } /// By returning false you can hide the following sensor calibration pages @@ -80,9 +86,11 @@ public: #if defined(__mobile__) virtual bool showOfflineMapExport () const { return false; } virtual bool showOfflineMapImport () const { return false; } + virtual bool useMobileFileDialog () const { return true;} #else virtual bool showOfflineMapExport () const { return true; } virtual bool showOfflineMapImport () const { return true; } + virtual bool useMobileFileDialog () const { return false;} #endif /// If returned QString in non-empty it means that firmware upgrade will run in a mode which only @@ -115,16 +123,19 @@ class CustomInstrumentWidget : public QObject public: //-- Widget Position enum Pos { - POS_TOP_RIGHT = 0, - POS_CENTER_RIGHT = 1, - POS_BOTTOM_RIGHT = 2, + POS_TOP_RIGHT, + POS_CENTER_RIGHT, + POS_BOTTOM_RIGHT, + POS_TOP_LEFT, + POS_CENTER_LEFT, + POS_BOTTOM_LEFT }; Q_ENUMS(Pos) CustomInstrumentWidget(QObject* parent = NULL); Q_PROPERTY(QUrl source READ source CONSTANT) Q_PROPERTY(Pos widgetPosition READ widgetPosition NOTIFY widgetPositionChanged) virtual QUrl source () { return QUrl(); } - virtual Pos widgetPosition () { return POS_CENTER_RIGHT; } + virtual Pos widgetPosition () { return POS_TOP_RIGHT; } signals: void widgetPositionChanged (); }; diff --git a/src/comm/LinkConfiguration.cc b/src/comm/LinkConfiguration.cc index 915c20cda6217c6c3fe51a135a4c7e4c95590651..c27239517cea29e159682d3c78ba52c2d2fa3473 100644 --- a/src/comm/LinkConfiguration.cc +++ b/src/comm/LinkConfiguration.cc @@ -37,6 +37,7 @@ LinkConfiguration::LinkConfiguration(const QString& name) , _name(name) , _dynamic(false) , _autoConnect(false) + , _highLatency(false) { _name = name; if (_name.isEmpty()) { @@ -50,6 +51,7 @@ LinkConfiguration::LinkConfiguration(LinkConfiguration* copy) _name = copy->name(); _dynamic = copy->isDynamic(); _autoConnect= copy->isAutoConnect(); + _highLatency= copy->isHighLatency(); Q_ASSERT(!_name.isEmpty()); } @@ -60,6 +62,7 @@ void LinkConfiguration::copyFrom(LinkConfiguration* source) _name = source->name(); _dynamic = source->isDynamic(); _autoConnect= source->isAutoConnect(); + _highLatency= source->isHighLatency(); } /*! diff --git a/src/comm/LinkConfiguration.h b/src/comm/LinkConfiguration.h index 76a4bed125f3d3f5f67d1c39c3731ce264a3a26a..8feda594d11c4cc3731a6354f733a6a28923cc30 100644 --- a/src/comm/LinkConfiguration.h +++ b/src/comm/LinkConfiguration.h @@ -33,6 +33,8 @@ public: Q_PROPERTY(bool autoConnect READ isAutoConnect WRITE setAutoConnect NOTIFY autoConnectChanged) Q_PROPERTY(bool autoConnectAllowed READ isAutoConnectAllowed CONSTANT) Q_PROPERTY(QString settingsURL READ settingsURL CONSTANT) + Q_PROPERTY(bool highLatency READ isHighLatency WRITE setHighLatency NOTIFY highLatencyChanged) + Q_PROPERTY(bool highLatencyAllowed READ isHighLatencyAllowed CONSTANT) // Property accessors @@ -76,6 +78,13 @@ public: */ bool isAutoConnect() { return _autoConnect; } + /*! + * + * Is this a High Latency configuration? + * @return True if this is an High Latency configuration (link with large delays). + */ + bool isHighLatency() { return _highLatency; } + /*! * Set if this is this a dynamic configuration. (decided at runtime) */ @@ -86,6 +95,11 @@ public: */ void setAutoConnect(bool autoc = true) { _autoConnect = autoc; emit autoConnectChanged(); } + /*! + * Set if this is this an High Latency configuration. + */ + void setHighLatency(bool hl = false) { _highLatency = hl; emit highLatencyChanged(); } + /// Virtual Methods /*! @@ -95,6 +109,13 @@ public: */ virtual bool isAutoConnectAllowed() { return false; } + /*! + * + * Is High Latency allowed for this type? + * @return True if this type can be set as an High Latency configuration + */ + virtual bool isHighLatencyAllowed() { return false; } + /*! * @brief Connection type * @@ -174,6 +195,7 @@ signals: void dynamicChanged (); void autoConnectChanged (); void linkChanged (LinkInterface* link); + void highLatencyChanged (); protected: LinkInterface* _link; ///< Link currently using this configuration (if any) @@ -181,6 +203,7 @@ private: QString _name; bool _dynamic; ///< A connection added automatically and not persistent (unless it's edited). bool _autoConnect; ///< This connection is started automatically at boot + bool _highLatency; }; typedef QSharedPointer SharedLinkConfigurationPointer; diff --git a/src/comm/LinkInterface.cc b/src/comm/LinkInterface.cc index f995fd10482bd51ff7010b06d8eb2697e65a1d51..f2920712efce1b076baafeee3637bcf9fca352c5 100644 --- a/src/comm/LinkInterface.cc +++ b/src/comm/LinkInterface.cc @@ -21,11 +21,12 @@ uint8_t LinkInterface::mavlinkChannel(void) const } // Links are only created by LinkManager so constructor is not public LinkInterface::LinkInterface(SharedLinkConfigurationPointer& config) - : QThread(0) - , _config(config) - , _mavlinkChannelSet(false) - , _active(false) - , _enableRateCollection(false) + : QThread (0) + , _config (config) + , _highLatency (config->isHighLatency()) + , _mavlinkChannelSet (false) + , _active (false) + , _enableRateCollection (false) , _decodedFirstMavlinkPacket(false) { _config->setLink(this); diff --git a/src/comm/LinkInterface.h b/src/comm/LinkInterface.h index 477b8a9e49593afcc8d0bb2da52b0fea823058eb..001dc0debb900d276d73280c108bb36e8025a304 100644 --- a/src/comm/LinkInterface.h +++ b/src/comm/LinkInterface.h @@ -116,6 +116,11 @@ public: /// set into the link when it is added to LinkManager uint8_t mavlinkChannel(void) const; + /// Returns whether this link is high latency or not. High latency links should only perform + /// minimal communication with vehicle. + /// signals: highLatencyChanged + bool highLatency(void) const { return _highLatency; } + bool decodedFirstMavlinkPacket(void) const { return _decodedFirstMavlinkPacket; } bool setDecodedFirstMavlinkPacket(bool decodedFirstMavlinkPacket) { return _decodedFirstMavlinkPacket = decodedFirstMavlinkPacket; } @@ -149,6 +154,7 @@ signals: void autoconnectChanged(bool autoconnect); void activeChanged(bool active); void _invokeWriteBytes(QByteArray); + void highLatencyChanged(bool highLatency); /// Signalled when a link suddenly goes away due to it being removed by for example pulling the cable to the connection. void connectionRemoved(LinkInterface* link); @@ -202,7 +208,8 @@ protected: void _logOutputDataRate(quint64 byteCount, qint64 time); SharedLinkConfigurationPointer _config; - + bool _highLatency; + private: /** * @brief logDataRateToBuffer Stores transmission times/amounts for statistics diff --git a/src/comm/LinkManager.cc b/src/comm/LinkManager.cc index e6b4506f2051aca89d6156c6f5fc40c7a85ebcf7..a425e1a81ef45e4f1e32b975dc55868cba74b8e0 100644 --- a/src/comm/LinkManager.cc +++ b/src/comm/LinkManager.cc @@ -27,6 +27,7 @@ #ifndef __mobile__ #include "GPSManager.h" +#include "PositionManager.h" #endif QGC_LOGGING_CATEGORY(LinkManagerLog, "LinkManagerLog") @@ -50,6 +51,9 @@ LinkManager::LinkManager(QGCApplication* app, QGCToolbox* toolbox) , _mavlinkChannelsUsedBitMask(1) // We never use channel 0 to avoid sequence numbering problems , _autoConnectSettings(NULL) , _mavlinkProtocol(NULL) +#ifndef __mobile__ + , _nmeaPort(NULL) +#endif { qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkManager", "Reference only"); qmlRegisterUncreatableType ("QGroundControl", 1, 0, "LinkConfiguration", "Reference only"); @@ -64,7 +68,9 @@ LinkManager::LinkManager(QGCApplication* app, QGCToolbox* toolbox) LinkManager::~LinkManager() { - +#ifndef __mobile__ + delete _nmeaPort; +#endif } void LinkManager::setToolbox(QGCToolbox *toolbox) @@ -500,6 +506,32 @@ void LinkManager::_updateAutoConnectLinks(void) QGCSerialPortInfo::BoardType_t boardType; QString boardName; +#ifndef __mobile__ + if (portInfo.systemLocation().trimmed() == _autoConnectSettings->autoConnectNmeaPort()->cookedValueString()) { + if (portInfo.systemLocation().trimmed() != _nmeaDeviceName) { + _nmeaDeviceName = portInfo.systemLocation().trimmed(); + qCDebug(LinkManagerLog) << "Configuring nmea port" << _nmeaDeviceName; + QSerialPort* newPort = new QSerialPort(portInfo); + + _nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt(); + newPort->setBaudRate(_nmeaBaud); + qCDebug(LinkManagerLog) << "Configuring nmea baudrate" << _nmeaBaud; + + // This will stop polling old device if previously set + _toolbox->qgcPositionManager()->setNmeaSourceDevice(newPort); + + if (_nmeaPort) { + delete _nmeaPort; + } + _nmeaPort = newPort; + + } else if (_autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt() != _nmeaBaud) { + _nmeaBaud = _autoConnectSettings->autoConnectNmeaBaud()->cookedValue().toUInt(); + _nmeaPort->setBaudRate(_nmeaBaud); + qCDebug(LinkManagerLog) << "Configuring nmea baudrate" << _nmeaBaud; + } + } else +#endif if (portInfo.getBoardInfo(boardType, boardName)) { if (portInfo.isBootloader()) { // Don't connect to bootloader @@ -613,11 +645,13 @@ void LinkManager::_updateAutoConnectLinks(void) } // Check for RTK GPS connection gone +#if !defined(__mobile__) if (!_autoConnectRTKPort.isEmpty() && !currentPorts.contains(_autoConnectRTKPort)) { qCDebug(LinkManagerLog) << "RTK GPS disconnected" << _autoConnectRTKPort; _toolbox->gpsManager()->disconnectGPS(); _autoConnectRTKPort.clear(); } +#endif #endif #endif // NO_SERIAL_LINK diff --git a/src/comm/LinkManager.h b/src/comm/LinkManager.h index b694d6dc386fee617cbda9ae69badf3ac51ae152..bd719e364305cc1dc8048d5b55a75af7ec4521e7 100644 --- a/src/comm/LinkManager.h +++ b/src/comm/LinkManager.h @@ -233,6 +233,13 @@ private: static const char* _defaultUPDLinkName; static const int _autoconnectUpdateTimerMSecs; static const int _autoconnectConnectDelayMSecs; + + // NMEA GPS device for GCS position +#ifndef __mobile__ + QString _nmeaDeviceName; + QSerialPort* _nmeaPort; + uint32_t _nmeaBaud; +#endif }; #endif diff --git a/src/comm/MAVLinkProtocol.cc b/src/comm/MAVLinkProtocol.cc index 9052ad608fcf52869de9edf186231c3692faac7a..d517a9a67b50b28e22c67f4a5dc94af341edf060 100644 --- a/src/comm/MAVLinkProtocol.cc +++ b/src/comm/MAVLinkProtocol.cc @@ -267,11 +267,17 @@ void MAVLinkProtocol::receiveBytes(LinkInterface* link, QByteArray b) } if (message.msgid == MAVLINK_MSG_ID_HEARTBEAT) { - // Start loggin on first heartbeat _startLogging(); mavlink_heartbeat_t heartbeat; mavlink_msg_heartbeat_decode(&message, &heartbeat); - emit vehicleHeartbeatInfo(link, message.sysid, message.compid, heartbeat.mavlink_version, heartbeat.autopilot, heartbeat.type); + emit vehicleHeartbeatInfo(link, message.sysid, message.compid, heartbeat.autopilot, heartbeat.type); + } + + if (message.msgid == MAVLINK_MSG_ID_HIGH_LATENCY2) { + _startLogging(); + mavlink_high_latency2_t highLatency2; + mavlink_msg_high_latency2_decode(&message, &highLatency2); + emit vehicleHeartbeatInfo(link, message.sysid, message.compid, highLatency2.autopilot, highLatency2.type); } // Detect if we are talking to an old radio not supporting v2 diff --git a/src/comm/MAVLinkProtocol.h b/src/comm/MAVLinkProtocol.h index 5d6311734020c37b270cc08cb8656cb58c0f337a..4d6ae9f79904e681c6f342a50a05c12d91b3cb0b 100644 --- a/src/comm/MAVLinkProtocol.h +++ b/src/comm/MAVLinkProtocol.h @@ -137,7 +137,7 @@ protected: signals: /// Heartbeat received on link - void vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleMavlinkVersion, int vehicleFirmwareType, int vehicleType); + void vehicleHeartbeatInfo(LinkInterface* link, int vehicleId, int componentId, int vehicleFirmwareType, int vehicleType); /** @brief Message received and directly copied via signal */ void messageReceived(LinkInterface* link, mavlink_message_t message); diff --git a/src/comm/MockLink.cc b/src/comm/MockLink.cc index c9872fef87e8c3693fd4202c3bfe97c81da61c9e..9ad225d6a9e48914c7d531b5aaf8cf026421bdc5 100644 --- a/src/comm/MockLink.cc +++ b/src/comm/MockLink.cc @@ -13,7 +13,7 @@ #include "QGCApplication.h" #ifdef UNITTEST_BUILD - #include "UnitTest.h" +#include "UnitTest.h" #endif #include @@ -44,6 +44,7 @@ const char* MockLink::_failParam = "COM_FLTMODE6"; const char* MockConfiguration::_firmwareTypeKey = "FirmwareType"; const char* MockConfiguration::_vehicleTypeKey = "VehicleType"; const char* MockConfiguration::_sendStatusTextKey = "SendStatusText"; +const char* MockConfiguration::_highLatencyKey = "HighLatency"; const char* MockConfiguration::_failureModeKey = "FailureMode"; MockLink::MockLink(SharedLinkConfigurationPointer& config) @@ -79,6 +80,7 @@ MockLink::MockLink(SharedLinkConfigurationPointer& config) _firmwareType = mockConfig->firmwareType(); _vehicleType = mockConfig->vehicleType(); _sendStatusText = mockConfig->sendStatusText(); + _highLatency = mockConfig->highLatency(); _failureMode = mockConfig->failureMode(); union px4_custom_mode px4_cm; @@ -164,27 +166,35 @@ void MockLink::run(void) void MockLink::_run1HzTasks(void) { if (_mavlinkStarted && _connected) { - _sendVibration(); - _sendADSBVehicles(); - if (!qgcApp()->runningUnitTests()) { - // Sending RC Channels during unit test breaks RC tests which does it's own RC simulation - _sendRCChannels(); - } - if (_sendHomePositionDelayCount > 0) { - // We delay home position for better testing - _sendHomePositionDelayCount--; + if (_highLatency) { + _sendHighLatency2(); } else { - _sendHomePosition(); - } - if (_sendStatusText) { - _sendStatusText = false; - _sendStatusTextMessages(); + _sendVibration(); + _sendADSBVehicles(); + if (!qgcApp()->runningUnitTests()) { + // Sending RC Channels during unit test breaks RC tests which does it's own RC simulation + _sendRCChannels(); + } + if (_sendHomePositionDelayCount > 0) { + // We delay home position for better testing + _sendHomePositionDelayCount--; + } else { + _sendHomePosition(); + } + if (_sendStatusText) { + _sendStatusText = false; + _sendStatusTextMessages(); + } } } } void MockLink::_run10HzTasks(void) { + if (_highLatency) { + return; + } + if (_mavlinkStarted && _connected) { _sendHeartBeat(); if (_sendGPSPositionDelayCount > 0) { @@ -198,6 +208,10 @@ void MockLink::_run10HzTasks(void) void MockLink::_run500HzTasks(void) { + if (_highLatency) { + return; + } + if (_mavlinkStarted && _connected) { _paramRequestListWorker(); _logDownloadWorker(); @@ -294,6 +308,46 @@ void MockLink::_sendHeartBeat(void) respondWithMavlinkMessage(msg); } +void MockLink::_sendHighLatency2(void) +{ + mavlink_message_t msg; + + union px4_custom_mode px4_cm; + px4_cm.data = _mavCustomMode; + + qDebug() << "Sending" << _mavCustomMode; + mavlink_msg_high_latency2_pack_chan(_vehicleSystemId, + _vehicleComponentId, + _mavlinkChannel, + &msg, + 0, // timestamp + _vehicleType, // MAV_TYPE + _firmwareType, // MAV_AUTOPILOT + px4_cm.custom_mode_hl, // custom_mode + (int32_t)(_vehicleLatitude * 1E7), + (int32_t)(_vehicleLongitude * 1E7), + (int16_t)_vehicleAltitude, + (int16_t)_vehicleAltitude, // target_altitude, + 0, // heading + 0, // target_heading + 0, // target_distance + 0, // throttle + 0, // airspeed + 0, // airspeed_sp + 0, // groundspeed + 0, // windspeed, + 0, // wind_heading + UINT8_MAX, // eph not known + UINT8_MAX, // epv not known + 0, // temperature_air + 0, // climb_rate + -1, // battery, do not use? + 0, // wp_num + 0, // failure_flags + 0, 0, 0); // custom0, custom1, custom2 + respondWithMavlinkMessage(msg); +} + void MockLink::_sendVibration(void) { mavlink_message_t msg; @@ -840,8 +894,8 @@ void MockLink::_handleCommandLong(const mavlink_message_t& msg) case MAV_CMD_USER_3: // Test command which returns MAV_RESULT_ACCEPTED on second attempt if (firstCmdUser3) { - firstCmdUser3 = false; - return; + firstCmdUser3 = false; + return; } else { firstCmdUser3 = true; commandResult = MAV_RESULT_ACCEPTED; @@ -850,8 +904,8 @@ void MockLink::_handleCommandLong(const mavlink_message_t& msg) case MAV_CMD_USER_4: // Test command which returns MAV_RESULT_FAILED on second attempt if (firstCmdUser4) { - firstCmdUser4 = false; - return; + firstCmdUser4 = false; + return; } else { firstCmdUser4 = true; commandResult = MAV_RESULT_FAILED; @@ -883,18 +937,21 @@ void MockLink::_respondWithAutopilotVersion(void) uint8_t customVersion[8] = { }; uint32_t flightVersion = 0; +#if !defined(NO_ARDUPILOT_DIALECT) if (_firmwareType == MAV_AUTOPILOT_ARDUPILOTMEGA) { flightVersion |= 3 << (8*3); flightVersion |= 5 << (8*2); flightVersion |= 0 << (8*1); flightVersion |= FIRMWARE_VERSION_TYPE_DEV << (8*0); } else if (_firmwareType == MAV_AUTOPILOT_PX4) { +#endif flightVersion |= 1 << (8*3); flightVersion |= 4 << (8*2); flightVersion |= 1 << (8*1); flightVersion |= FIRMWARE_VERSION_TYPE_DEV << (8*0); +#if !defined(NO_ARDUPILOT_DIALECT) } - +#endif mavlink_msg_autopilot_version_pack_chan(_vehicleSystemId, _vehicleComponentId, _mavlinkChannel, @@ -909,7 +966,8 @@ void MockLink::_respondWithAutopilotVersion(void) (uint8_t *)&customVersion, // os_custom_version, 0, // vendor_id, 0, // product_id, - 0); // uid + 0, // uid + 0); // uid2 respondWithMavlinkMessage(msg); } @@ -937,8 +995,8 @@ void MockLink::_sendHomePosition(void) (int32_t)(_vehicleAltitude * 1000), 0.0f, 0.0f, 0.0f, &bogus[0], - 0.0f, 0.0f, 0.0f, - 0); + 0.0f, 0.0f, 0.0f, + 0); respondWithMavlinkMessage(msg); } @@ -966,7 +1024,7 @@ void MockLink::_sendGpsRawInt(void) 0, // Altitude uncertainty in meters * 1000 (positive for up). 0, // Speed uncertainty in meters * 1000 (positive for up). 0); // Heading / track uncertainty in degrees * 1e5. - respondWithMavlinkMessage(msg); + respondWithMavlinkMessage(msg); } void MockLink::_sendStatusTextMessages(void) @@ -1004,10 +1062,11 @@ void MockLink::_sendStatusTextMessages(void) MockConfiguration::MockConfiguration(const QString& name) : LinkConfiguration(name) - , _firmwareType(MAV_AUTOPILOT_PX4) - , _vehicleType(MAV_TYPE_QUADROTOR) - , _sendStatusText(false) - , _failureMode(FailNone) + , _firmwareType (MAV_AUTOPILOT_PX4) + , _vehicleType (MAV_TYPE_QUADROTOR) + , _sendStatusText (false) + , _highLatency (false) + , _failureMode (FailNone) { } @@ -1018,6 +1077,7 @@ MockConfiguration::MockConfiguration(MockConfiguration* source) _firmwareType = source->_firmwareType; _vehicleType = source->_vehicleType; _sendStatusText = source->_sendStatusText; + _highLatency = source->_highLatency; _failureMode = source->_failureMode; } @@ -1034,6 +1094,7 @@ void MockConfiguration::copyFrom(LinkConfiguration *source) _firmwareType = usource->_firmwareType; _vehicleType = usource->_vehicleType; _sendStatusText = usource->_sendStatusText; + _highLatency = usource->_highLatency; _failureMode = usource->_failureMode; } @@ -1043,6 +1104,7 @@ void MockConfiguration::saveSettings(QSettings& settings, const QString& root) settings.setValue(_firmwareTypeKey, (int)_firmwareType); settings.setValue(_vehicleTypeKey, (int)_vehicleType); settings.setValue(_sendStatusTextKey, _sendStatusText); + settings.setValue(_highLatencyKey, _highLatency); settings.setValue(_failureModeKey, (int)_failureMode); settings.sync(); settings.endGroup(); @@ -1054,6 +1116,7 @@ void MockConfiguration::loadSettings(QSettings& settings, const QString& root) _firmwareType = (MAV_AUTOPILOT)settings.value(_firmwareTypeKey, (int)MAV_AUTOPILOT_PX4).toInt(); _vehicleType = (MAV_TYPE)settings.value(_vehicleTypeKey, (int)MAV_TYPE_QUADROTOR).toInt(); _sendStatusText = settings.value(_sendStatusTextKey, false).toBool(); + _highLatency = settings.value(_highLatencyKey, false).toBool(); _failureMode = (FailureMode_t)settings.value(_failureModeKey, (int)FailNone).toInt(); settings.endGroup(); } @@ -1226,9 +1289,9 @@ void MockLink::_handleLogRequestData(const mavlink_message_t& msg) mavlink_msg_log_request_data_decode(&msg, &request); if (_logDownloadFilename.isEmpty()) { - #ifdef UNITTEST_BUILD +#ifdef UNITTEST_BUILD _logDownloadFilename = UnitTest::createRandomFile(_logDownloadFileSize); - #endif +#endif } if (request.id != 0) { diff --git a/src/comm/MockLink.h b/src/comm/MockLink.h index 1b17d6a75eeb0c5c0014d069ff6dda5957cd1826..105839a0d2a761874f336ddee07bb1f7b394b070 100644 --- a/src/comm/MockLink.h +++ b/src/comm/MockLink.h @@ -32,12 +32,15 @@ public: Q_PROPERTY(int firmware READ firmware WRITE setFirmware NOTIFY firmwareChanged) Q_PROPERTY(int vehicle READ vehicle WRITE setVehicle NOTIFY vehicleChanged) Q_PROPERTY(bool sendStatus READ sendStatusText WRITE setSendStatusText NOTIFY sendStatusChanged) + Q_PROPERTY(bool highLatency READ highLatency WRITE setHighLatency NOTIFY highLatencyChanged) // QML Access int firmware () { return (int)_firmwareType; } void setFirmware (int type) { _firmwareType = (MAV_AUTOPILOT)type; emit firmwareChanged(); } int vehicle () { return (int)_vehicleType; } + bool highLatency () const { return _highLatency; } void setVehicle (int type) { _vehicleType = (MAV_TYPE)type; emit vehicleChanged(); } + void setHighLatency (bool latency) { _highLatency = latency; emit highLatencyChanged(); } MockConfiguration(const QString& name); MockConfiguration(MockConfiguration* source); @@ -73,16 +76,19 @@ signals: void firmwareChanged (); void vehicleChanged (); void sendStatusChanged (); + void highLatencyChanged (); private: MAV_AUTOPILOT _firmwareType; MAV_TYPE _vehicleType; bool _sendStatusText; + bool _highLatency; FailureMode_t _failureMode; static const char* _firmwareTypeKey; static const char* _vehicleTypeKey; static const char* _sendStatusTextKey; + static const char* _highLatencyKey; static const char* _failureModeKey; }; @@ -169,6 +175,7 @@ private: // MockLink methods void _sendHeartBeat(void); + void _sendHighLatency2(void); void _handleIncomingNSHBytes(const char* bytes, int cBytes); void _handleIncomingMavlinkBytes(const uint8_t* bytes, int cBytes); void _loadParams(void); diff --git a/src/comm/PX4MockLink.params b/src/comm/PX4MockLink.params index 3ae08c462d27bac6f42ef3eea2d9ae85f1d51cad..39592dac7d3c806281cf57290f4eb8b9b75f4260 100644 --- a/src/comm/PX4MockLink.params +++ b/src/comm/PX4MockLink.params @@ -1,11 +1,17 @@ -# Onboard parameters for vehicle 1 +# Onboard parameters for Vehicle 1 # -# MAV ID COMPONENT ID PARAM NAME VALUE (FLOAT) +# Stack: PX4 Pro +# Vehicle: Multi-Rotor +# Version: 1.7.2 dev +# Git Revision: 1cd0ca9c6cdded48 +# +# Vehicle-Id Component-Id Name Value Type 1 1 BAT_A_PER_V 15.391030311584472656 9 1 1 BAT_CAPACITY -1.000000000000000000 9 1 1 BAT_CNT_V_CURR 0.000805664050858468 9 1 1 BAT_CNT_V_VOLT 0.000805664050858468 9 1 1 BAT_CRIT_THR 0.070000000298023224 9 +1 1 BAT_EMERGEN_THR 0.050000000745058060 9 1 1 BAT_LOW_THR 0.150000005960464478 9 1 1 BAT_N_CELLS 3 6 1 1 BAT_R_INTERNAL -1.000000000000000000 9 @@ -15,49 +21,66 @@ 1 1 BAT_V_EMPTY 3.400000095367431641 9 1 1 BAT_V_LOAD_DROP 0.300000011920928955 9 1 1 BAT_V_OFFS_CURR 0.000000000000000000 9 -1 1 BAT_V_SCALE_IO 10000 6 -1 1 CAL_ACC0_ID 1246218 6 -1 1 CAL_ACC1_ID 1114634 6 -1 1 CAL_ACC1_XOFF 0.982475280761718750 9 -1 1 CAL_ACC1_XSCALE 0.977573156356811523 9 -1 1 CAL_ACC1_YOFF 0.922321319580078125 9 -1 1 CAL_ACC1_YSCALE 1.008152961730957031 9 -1 1 CAL_ACC1_ZOFF 1.137038230895996094 9 -1 1 CAL_ACC1_ZSCALE 1.007824659347534180 9 -1 1 CAL_ACC2_ID 0 6 +1 1 CAL_ACC0_EN 1 6 +1 1 CAL_ACC0_ID 1376264 6 +1 1 CAL_ACC0_XOFF 0.001000000047497451 9 +1 1 CAL_ACC0_XSCALE 1.000100016593933105 9 +1 1 CAL_ACC0_YOFF -0.001000000047497451 9 +1 1 CAL_ACC0_YSCALE 1.000100016593933105 9 +1 1 CAL_ACC0_ZOFF 0.001000000047497451 9 +1 1 CAL_ACC0_ZSCALE 1.000100016593933105 9 +1 1 CAL_ACC1_EN 1 6 +1 1 CAL_ACC1_ID 1310728 6 +1 1 CAL_ACC1_XOFF 0.001000000047497451 9 +1 1 CAL_ACC1_XSCALE 1.000000000000000000 9 +1 1 CAL_ACC1_YOFF 0.000000000000000000 9 +1 1 CAL_ACC1_YSCALE 1.000000000000000000 9 +1 1 CAL_ACC1_ZOFF 0.000000000000000000 9 +1 1 CAL_ACC1_ZSCALE 1.000000000000000000 9 1 1 CAL_ACC2_XOFF 0.000000000000000000 9 1 1 CAL_ACC2_XSCALE 1.000000000000000000 9 1 1 CAL_ACC2_YOFF 0.000000000000000000 9 1 1 CAL_ACC2_YSCALE 1.000000000000000000 9 1 1 CAL_ACC2_ZOFF 0.000000000000000000 9 1 1 CAL_ACC2_ZSCALE 1.000000000000000000 9 -1 1 CAL_ACC_PRIME 1246218 6 +1 1 CAL_ACC_PRIME 0 6 +1 1 CAL_AIR_CMODEL 0 6 +1 1 CAL_AIR_TUBED_MM 1.500000000000000000 9 +1 1 CAL_AIR_TUBELEN 0.200000002980232239 9 1 1 CAL_BARO_PRIME 0 6 -1 1 CAL_GYRO0_ID 2162688 6 -1 1 CAL_GYRO1_ID 2228490 6 -1 1 CAL_GYRO1_XOFF 0.015412596054375172 9 +1 1 CAL_GYRO0_EN 1 6 +1 1 CAL_GYRO0_ID 2293768 6 +1 1 CAL_GYRO0_XOFF 0.001000000047497451 9 +1 1 CAL_GYRO0_XSCALE 1.000000000000000000 9 +1 1 CAL_GYRO0_YOFF 0.000000000000000000 9 +1 1 CAL_GYRO0_YSCALE 1.000000000000000000 9 +1 1 CAL_GYRO0_ZOFF 0.000000000000000000 9 +1 1 CAL_GYRO0_ZSCALE 1.000000000000000000 9 +1 1 CAL_GYRO1_XOFF 0.000000000000000000 9 1 1 CAL_GYRO1_XSCALE 1.000000000000000000 9 -1 1 CAL_GYRO1_YOFF 0.004993732087314129 9 +1 1 CAL_GYRO1_YOFF 0.000000000000000000 9 1 1 CAL_GYRO1_YSCALE 1.000000000000000000 9 -1 1 CAL_GYRO1_ZOFF -0.027027824893593788 9 +1 1 CAL_GYRO1_ZOFF 0.000000000000000000 9 1 1 CAL_GYRO1_ZSCALE 1.000000000000000000 9 -1 1 CAL_GYRO2_ID 0 6 1 1 CAL_GYRO2_XOFF 0.000000000000000000 9 1 1 CAL_GYRO2_XSCALE 1.000000000000000000 9 1 1 CAL_GYRO2_YOFF 0.000000000000000000 9 1 1 CAL_GYRO2_YSCALE 1.000000000000000000 9 1 1 CAL_GYRO2_ZOFF 0.000000000000000000 9 1 1 CAL_GYRO2_ZSCALE 1.000000000000000000 9 -1 1 CAL_GYRO_PRIME 2162688 6 -1 1 CAL_MAG0_ID 73225 6 -1 1 CAL_MAG0_ROT 0 6 -1 1 CAL_MAG1_ID 131594 6 +1 1 CAL_GYRO_PRIME 0 6 +1 1 CAL_MAG0_EN 1 6 +1 1 CAL_MAG0_ID 196616 6 +1 1 CAL_MAG0_ROT -1 6 +1 1 CAL_MAG0_XOFF 0.001000000047497451 9 +1 1 CAL_MAG1_EN 1 6 +1 1 CAL_MAG1_ID 0 6 1 1 CAL_MAG1_ROT -1 6 -1 1 CAL_MAG1_XOFF 0.010499725118279457 9 +1 1 CAL_MAG1_XOFF 0.000000000000000000 9 1 1 CAL_MAG1_XSCALE 1.000000000000000000 9 -1 1 CAL_MAG1_YOFF 0.041090622544288635 9 +1 1 CAL_MAG1_YOFF 0.000000000000000000 9 1 1 CAL_MAG1_YSCALE 1.000000000000000000 9 -1 1 CAL_MAG1_ZOFF 0.046924047172069550 9 +1 1 CAL_MAG1_ZOFF 0.000000000000000000 9 1 1 CAL_MAG1_ZSCALE 1.000000000000000000 9 1 1 CAL_MAG2_ID 0 6 1 1 CAL_MAG2_ROT -1 6 @@ -67,32 +90,44 @@ 1 1 CAL_MAG2_YSCALE 1.000000000000000000 9 1 1 CAL_MAG2_ZOFF 0.000000000000000000 9 1 1 CAL_MAG2_ZSCALE 1.000000000000000000 9 -1 1 CAL_MAG_PRIME 73225 6 +1 1 CAL_MAG3_ID 0 6 +1 1 CAL_MAG3_ROT -1 6 +1 1 CAL_MAG3_XOFF 0.000000000000000000 9 +1 1 CAL_MAG3_XSCALE 1.000000000000000000 9 +1 1 CAL_MAG3_YOFF 0.000000000000000000 9 +1 1 CAL_MAG3_YSCALE 1.000000000000000000 9 +1 1 CAL_MAG3_ZOFF 0.000000000000000000 9 +1 1 CAL_MAG3_ZSCALE 1.000000000000000000 9 +1 1 CAL_MAG_PRIME 0 6 1 1 CAL_MAG_SIDES 63 6 1 1 CBRK_AIRSPD_CHK 0 6 -1 1 CBRK_BUZZER 0 6 1 1 CBRK_ENGINEFAIL 284953 6 1 1 CBRK_FLIGHTTERM 121212 6 1 1 CBRK_GPSFAIL 0 6 -1 1 CBRK_IO_SAFETY 0 6 +1 1 CBRK_RATE_CTRL 0 6 1 1 CBRK_SUPPLY_CHK 0 6 1 1 CBRK_USB_CHK 0 6 -1 1 COM_ARM_EKF_AB 0.004999999888241291 9 +1 1 CBRK_VELPOSERR 0 6 +1 1 COM_ARM_AUTH 256010 6 +1 1 COM_ARM_EKF_AB 0.002400000113993883 9 1 1 COM_ARM_EKF_GB 0.000869999988935888 9 1 1 COM_ARM_EKF_HGT 1.000000000000000000 9 1 1 COM_ARM_EKF_POS 0.500000000000000000 9 1 1 COM_ARM_EKF_VEL 0.500000000000000000 9 1 1 COM_ARM_EKF_YAW 0.500000000000000000 9 1 1 COM_ARM_IMU_ACC 0.699999988079071045 9 -1 1 COM_ARM_IMU_GYR 0.090000003576278687 9 +1 1 COM_ARM_IMU_GYR 0.250000000000000000 9 +1 1 COM_ARM_MAG 0.150000005960464478 9 +1 1 COM_ARM_MIS_REQ 0 6 +1 1 COM_ARM_SWISBTN 0 6 1 1 COM_ARM_WO_GPS 1 6 -1 1 COM_AUTOS_PAR 1 6 -1 1 COM_DISARM_LAND 0 6 +1 1 COM_DISARM_LAND 3 6 1 1 COM_DL_LOSS_T 10 6 1 1 COM_DL_REG_T 0 6 1 1 COM_EF_C2T 5.000000000000000000 9 1 1 COM_EF_THROT 0.500000000000000000 9 1 1 COM_EF_TIME 10.000000000000000000 9 +1 1 COM_FLIGHT_UUID 1 6 1 1 COM_FLTMODE1 -1 6 1 1 COM_FLTMODE2 -1 6 1 1 COM_FLTMODE3 -1 6 @@ -105,49 +140,256 @@ 1 1 COM_OBL_ACT 0 6 1 1 COM_OBL_RC_ACT 0 6 1 1 COM_OF_LOSS_T 0.000000000000000000 9 +1 1 COM_POSCTL_NAVL 0 6 +1 1 COM_POS_FS_DELAY 1 6 +1 1 COM_POS_FS_GAIN 10 6 +1 1 COM_POS_FS_PROB 30 6 1 1 COM_RC_ARM_HYST 1000 6 -1 1 COM_RC_IN_MODE 0 6 +1 1 COM_RC_IN_MODE 1 6 1 1 COM_RC_LOSS_T 0.500000000000000000 9 -1 1 EKF2_REC_RPL 0 6 -1 1 FW_AIRSPD_TRIM 15.000000000000000000 9 +1 1 COM_RC_OVERRIDE 0 6 +1 1 COM_RC_STICK_OV 12.000000000000000000 9 +1 1 COM_TAKEOFF_ACT 0 6 +1 1 EKF2_ABIAS_INIT 0.200000002980232239 9 +1 1 EKF2_ABL_ACCLIM 25.000000000000000000 9 +1 1 EKF2_ABL_GYRLIM 3.000000000000000000 9 +1 1 EKF2_ABL_LIM 0.400000005960464478 9 +1 1 EKF2_ABL_TAU 0.500000000000000000 9 +1 1 EKF2_ACC_B_NOISE 0.003000000026077032 9 +1 1 EKF2_ACC_NOISE 0.349999994039535522 9 +1 1 EKF2_AID_MASK 1 6 +1 1 EKF2_ANGERR_INIT 0.009999999776482582 9 +1 1 EKF2_ARSP_THR 0.000000000000000000 9 +1 1 EKF2_ASPD_MAX 20.000000000000000000 9 +1 1 EKF2_ASP_DELAY 100.000000000000000000 9 +1 1 EKF2_BARO_DELAY 0.000000000000000000 9 +1 1 EKF2_BARO_GATE 5.000000000000000000 9 +1 1 EKF2_BARO_NOISE 2.000000000000000000 9 +1 1 EKF2_BCOEF_X 25.000000000000000000 9 +1 1 EKF2_BCOEF_Y 25.000000000000000000 9 +1 1 EKF2_BETA_GATE 5.000000000000000000 9 +1 1 EKF2_BETA_NOISE 0.300000011920928955 9 +1 1 EKF2_DECL_TYPE 7 6 +1 1 EKF2_DRAG_NOISE 2.500000000000000000 9 +1 1 EKF2_EAS_NOISE 1.399999976158142090 9 +1 1 EKF2_EVA_NOISE 0.050000000745058060 9 +1 1 EKF2_EVP_NOISE 0.050000000745058060 9 +1 1 EKF2_EV_DELAY 175.000000000000000000 9 +1 1 EKF2_EV_GATE 5.000000000000000000 9 +1 1 EKF2_EV_POS_X 0.000000000000000000 9 +1 1 EKF2_EV_POS_Y 0.000000000000000000 9 +1 1 EKF2_EV_POS_Z 0.000000000000000000 9 +1 1 EKF2_FUSE_BETA 0 6 +1 1 EKF2_GBIAS_INIT 0.009999999776482582 9 +1 1 EKF2_GPS_CHECK 21 6 +1 1 EKF2_GPS_DELAY 110.000000000000000000 9 +1 1 EKF2_GPS_POS_X 0.000000000000000000 9 +1 1 EKF2_GPS_POS_Y 0.000000000000000000 9 +1 1 EKF2_GPS_POS_Z 0.000000000000000000 9 +1 1 EKF2_GPS_P_GATE 5.000000000000000000 9 +1 1 EKF2_GPS_P_NOISE 0.500000000000000000 9 +1 1 EKF2_GPS_V_GATE 5.000000000000000000 9 +1 1 EKF2_GPS_V_NOISE 0.500000000000000000 9 +1 1 EKF2_GYR_B_NOISE 0.001000000047497451 9 +1 1 EKF2_GYR_NOISE 0.014999999664723873 9 +1 1 EKF2_HDG_GATE 2.599999904632568359 9 +1 1 EKF2_HEAD_NOISE 0.300000011920928955 9 +1 1 EKF2_HGT_MODE 0 6 +1 1 EKF2_IMU_POS_X 0.000000000000000000 9 +1 1 EKF2_IMU_POS_Y 0.000000000000000000 9 +1 1 EKF2_IMU_POS_Z 0.000000000000000000 9 +1 1 EKF2_MAGBIAS_ID 0 6 +1 1 EKF2_MAGBIAS_X 0.000000000000000000 9 +1 1 EKF2_MAGBIAS_Y 0.000000000000000000 9 +1 1 EKF2_MAGBIAS_Z 0.000000000000000000 9 +1 1 EKF2_MAGB_K 0.200000002980232239 9 +1 1 EKF2_MAGB_VREF 0.000000249999999369 9 +1 1 EKF2_MAG_ACCLIM 0.500000000000000000 9 +1 1 EKF2_MAG_B_NOISE 0.000099999997473788 9 +1 1 EKF2_MAG_DECL 0.000000000000000000 9 +1 1 EKF2_MAG_DELAY 0.000000000000000000 9 +1 1 EKF2_MAG_E_NOISE 0.001000000047497451 9 +1 1 EKF2_MAG_GATE 3.000000000000000000 9 +1 1 EKF2_MAG_NOISE 0.050000000745058060 9 +1 1 EKF2_MAG_TYPE 1 6 +1 1 EKF2_MAG_YAWLIM 0.250000000000000000 9 +1 1 EKF2_MIN_OBS_DT 20 6 +1 1 EKF2_MIN_RNG 0.100000001490116119 9 +1 1 EKF2_NOAID_NOISE 10.000000000000000000 9 +1 1 EKF2_OF_DELAY 5.000000000000000000 9 +1 1 EKF2_OF_GATE 3.000000000000000000 9 +1 1 EKF2_OF_N_MAX 0.500000000000000000 9 +1 1 EKF2_OF_N_MIN 0.150000005960464478 9 +1 1 EKF2_OF_POS_X 0.000000000000000000 9 +1 1 EKF2_OF_POS_Y 0.000000000000000000 9 +1 1 EKF2_OF_POS_Z 0.000000000000000000 9 +1 1 EKF2_OF_QMIN 1 6 +1 1 EKF2_OF_RMAX 2.500000000000000000 9 +1 1 EKF2_PCOEF_XN 0.000000000000000000 9 +1 1 EKF2_PCOEF_XP 0.000000000000000000 9 +1 1 EKF2_PCOEF_Y 0.000000000000000000 9 +1 1 EKF2_PCOEF_Z 0.000000000000000000 9 +1 1 EKF2_REQ_EPH 5.000000000000000000 9 +1 1 EKF2_REQ_EPV 8.000000000000000000 9 +1 1 EKF2_REQ_GDOP 2.500000000000000000 9 +1 1 EKF2_REQ_HDRIFT 0.300000011920928955 9 +1 1 EKF2_REQ_NSATS 6 6 +1 1 EKF2_REQ_SACC 1.000000000000000000 9 +1 1 EKF2_REQ_VDRIFT 0.500000000000000000 9 +1 1 EKF2_RNG_AID 0 6 +1 1 EKF2_RNG_A_HMAX 5.000000000000000000 9 +1 1 EKF2_RNG_A_IGATE 1.000000000000000000 9 +1 1 EKF2_RNG_A_VMAX 1.000000000000000000 9 +1 1 EKF2_RNG_DELAY 5.000000000000000000 9 +1 1 EKF2_RNG_GATE 5.000000000000000000 9 +1 1 EKF2_RNG_NOISE 0.100000001490116119 9 +1 1 EKF2_RNG_PITCH 0.000000000000000000 9 +1 1 EKF2_RNG_POS_X 0.000000000000000000 9 +1 1 EKF2_RNG_POS_Y 0.000000000000000000 9 +1 1 EKF2_RNG_POS_Z 0.000000000000000000 9 +1 1 EKF2_RNG_SFE 0.050000000745058060 9 +1 1 EKF2_TAS_GATE 3.000000000000000000 9 +1 1 EKF2_TAU_POS 0.250000000000000000 9 +1 1 EKF2_TAU_VEL 0.250000000000000000 9 +1 1 EKF2_TERR_GRAD 0.500000000000000000 9 +1 1 EKF2_TERR_NOISE 5.000000000000000000 9 +1 1 EKF2_WIND_NOISE 0.100000001490116119 9 +1 1 FW_ARSP_MODE 0 6 1 1 FW_CLMBOUT_DIFF 10.000000000000000000 9 -1 1 FW_MAN_P_SC 1.000000000000000000 9 -1 1 FW_MAN_R_SC 1.000000000000000000 9 -1 1 FW_MAN_Y_SC 1.000000000000000000 9 -1 1 FW_THR_CRUISE 0.600000023841857910 9 1 1 GF_ACTION 1 6 1 1 GF_ALTMODE 0 6 1 1 GF_COUNT -1 6 1 1 GF_MAX_HOR_DIST 0.000000000000000000 9 1 1 GF_MAX_VER_DIST 0.000000000000000000 9 1 1 GF_SOURCE 0 6 -1 1 GPS_DUMP_COMM 0 6 -1 1 LED_RGB_MAXBRT 15 6 +1 1 LNDMC_ALT_MAX -1.000000000000000000 9 +1 1 LNDMC_FFALL_THR 2.000000000000000000 9 +1 1 LNDMC_FFALL_TTRI 0.300000011920928955 9 +1 1 LNDMC_ROT_MAX 20.000000000000000000 9 +1 1 LNDMC_THR_RANGE 0.100000001490116119 9 +1 1 LNDMC_XY_VEL_MAX 1.500000000000000000 9 +1 1 LNDMC_Z_VEL_MAX 0.500000000000000000 9 +1 1 LND_FLIGHT_T_HI 0 6 +1 1 LND_FLIGHT_T_LO 234294018 6 1 1 MAV_BROADCAST 0 6 1 1 MAV_COMP_ID 1 6 1 1 MAV_FWDEXTSP 1 6 -1 1 MAV_PROTO_VER 0 6 +1 1 MAV_PROTO_VER 2 6 1 1 MAV_RADIO_ID 0 6 1 1 MAV_SYS_ID 1 6 1 1 MAV_TEST_PAR 1 6 -1 1 MAV_TYPE 2 6 +1 1 MAV_TYPE 13 6 1 1 MAV_USEHILGPS 0 6 +1 1 MC_ACRO_EXPO 0.689999997615814209 9 +1 1 MC_ACRO_P_MAX 720.000000000000000000 9 +1 1 MC_ACRO_R_MAX 720.000000000000000000 9 +1 1 MC_ACRO_SUPEXPO 0.699999988079071045 9 +1 1 MC_ACRO_Y_MAX 540.000000000000000000 9 +1 1 MC_BAT_SCALE_EN 0 6 +1 1 MC_PITCHRATE_D 0.003000000026077032 9 +1 1 MC_PITCHRATE_FF 0.000000000000000000 9 +1 1 MC_PITCHRATE_I 0.050000000745058060 9 +1 1 MC_PITCHRATE_MAX 220.000000000000000000 9 +1 1 MC_PITCHRATE_P 0.100000001490116119 9 +1 1 MC_PITCH_P 6.000000000000000000 9 +1 1 MC_PITCH_TC 0.200000002980232239 9 +1 1 MC_PR_INT_LIM 0.300000011920928955 9 +1 1 MC_RATT_TH 0.800000011920928955 9 +1 1 MC_ROLLRATE_D 0.003000000026077032 9 +1 1 MC_ROLLRATE_FF 0.000000000000000000 9 +1 1 MC_ROLLRATE_I 0.050000000745058060 9 +1 1 MC_ROLLRATE_MAX 220.000000000000000000 9 +1 1 MC_ROLLRATE_P 0.100000001490116119 9 +1 1 MC_ROLL_P 6.000000000000000000 9 +1 1 MC_ROLL_TC 0.200000002980232239 9 +1 1 MC_RR_INT_LIM 0.300000011920928955 9 +1 1 MC_TPA_BREAK_D 1.000000000000000000 9 +1 1 MC_TPA_BREAK_I 1.000000000000000000 9 +1 1 MC_TPA_BREAK_P 1.000000000000000000 9 +1 1 MC_TPA_RATE_D 0.000000000000000000 9 +1 1 MC_TPA_RATE_I 0.000000000000000000 9 +1 1 MC_TPA_RATE_P 0.000000000000000000 9 +1 1 MC_YAWRATE_D 0.000000000000000000 9 +1 1 MC_YAWRATE_FF 0.000000000000000000 9 +1 1 MC_YAWRATE_I 0.100000001490116119 9 +1 1 MC_YAWRATE_MAX 200.000000000000000000 9 +1 1 MC_YAWRATE_P 0.200000002980232239 9 1 1 MC_YAWRAUTO_MAX 45.000000000000000000 9 +1 1 MC_YAW_FF 0.500000000000000000 9 +1 1 MC_YAW_P 2.799999952316284180 9 +1 1 MC_YR_INT_LIM 0.300000011920928955 9 1 1 MIS_ALTMODE 1 6 1 1 MIS_DIST_1WP 900.000000000000000000 9 -1 1 MIS_LTRMIN_ALT 1.200000047683715820 9 +1 1 MIS_DIST_WPS 900.000000000000000000 9 +1 1 MIS_LTRMIN_ALT 10.000000000000000000 9 1 1 MIS_ONBOARD_EN 1 6 1 1 MIS_TAKEOFF_ALT 2.500000000000000000 9 1 1 MIS_YAWMODE 1 6 1 1 MIS_YAW_ERR 12.000000000000000000 9 -1 1 MIS_YAW_TMT -1.000000000000000000 9 -1 1 MOT_SLEW_MAX 0.000000000000000000 9 +1 1 MIS_YAW_TMT 10.000000000000000000 9 +1 1 MNT_DO_STAB 0 6 +1 1 MNT_MAN_PITCH 0 6 +1 1 MNT_MAN_ROLL 0 6 +1 1 MNT_MAN_YAW 0 6 +1 1 MNT_MAV_COMPID 154 6 +1 1 MNT_MAV_SYSID 1 6 +1 1 MNT_MODE_IN 0 6 +1 1 MNT_MODE_OUT 0 6 +1 1 MNT_OB_LOCK_MODE 0.000000000000000000 9 +1 1 MNT_OB_NORM_MODE -1.000000000000000000 9 +1 1 MNT_OFF_PITCH 0.000000000000000000 9 +1 1 MNT_OFF_ROLL 0.000000000000000000 9 +1 1 MNT_OFF_YAW 0.000000000000000000 9 +1 1 MNT_RANGE_PITCH 360.000000000000000000 9 +1 1 MNT_RANGE_ROLL 360.000000000000000000 9 +1 1 MNT_RANGE_YAW 360.000000000000000000 9 +1 1 MPC_ACC_DOWN_MAX 10.000000000000000000 9 +1 1 MPC_ACC_HOR 5.000000000000000000 9 +1 1 MPC_ACC_HOR_MAX 2.000000000000000000 9 +1 1 MPC_ACC_UP_MAX 10.000000000000000000 9 +1 1 MPC_ALT_MODE 0 6 +1 1 MPC_CRUISE_90 3.000000000000000000 9 +1 1 MPC_DEC_HOR_SLOW 5.000000000000000000 9 +1 1 MPC_HOLD_DZ 0.100000001490116119 9 +1 1 MPC_HOLD_MAX_XY 0.800000011920928955 9 +1 1 MPC_HOLD_MAX_Z 2.000000000000000000 9 +1 1 MPC_JERK_MAX 0.000000000000000000 9 +1 1 MPC_JERK_MIN 1.000000000000000000 9 +1 1 MPC_LAND_ALT1 10.000000000000000000 9 +1 1 MPC_LAND_ALT2 5.000000000000000000 9 +1 1 MPC_LAND_SPEED 0.699999988079071045 9 +1 1 MPC_MANTHR_MAX 0.899999976158142090 9 +1 1 MPC_MANTHR_MIN 0.079999998211860657 9 +1 1 MPC_MAN_TILT_MAX 35.000000000000000000 9 +1 1 MPC_MAN_Y_MAX 200.000000000000000000 9 +1 1 MPC_THR_HOVER 0.500000000000000000 9 +1 1 MPC_THR_MAX 0.899999976158142090 9 +1 1 MPC_THR_MIN 0.119999997317790985 9 +1 1 MPC_TILTMAX_AIR 45.000000000000000000 9 +1 1 MPC_TILTMAX_LND 12.000000000000000000 9 +1 1 MPC_TKO_RAMP_T 0.400000005960464478 9 +1 1 MPC_TKO_SPEED 1.000000000000000000 9 +1 1 MPC_VELD_LP 5.000000000000000000 9 +1 1 MPC_VEL_MANUAL 10.000000000000000000 9 1 1 MPC_XY_CRUISE 5.000000000000000000 9 -1 1 NAV_ACC_RAD 10.000000000000000000 9 +1 1 MPC_XY_MAN_EXPO 0.000000000000000000 9 +1 1 MPC_XY_P 0.800000011920928955 9 +1 1 MPC_XY_VEL_D 0.004999999888241291 9 +1 1 MPC_XY_VEL_I 0.200000002980232239 9 +1 1 MPC_XY_VEL_MAX 12.000000000000000000 9 +1 1 MPC_XY_VEL_P 0.150000005960464478 9 +1 1 MPC_Z_MAN_EXPO 0.000000000000000000 9 +1 1 MPC_Z_P 1.000000000000000000 9 +1 1 MPC_Z_VEL_D 0.000000000000000000 9 +1 1 MPC_Z_VEL_I 0.150000005960464478 9 +1 1 MPC_Z_VEL_MAX_DN 1.500000000000000000 9 +1 1 MPC_Z_VEL_MAX_UP 3.000000000000000000 9 +1 1 MPC_Z_VEL_P 0.600000023841857910 9 +1 1 NAV_ACC_RAD 2.000000000000000000 9 1 1 NAV_AH_ALT 600.000000000000000000 9 1 1 NAV_AH_LAT -265847810 6 1 1 NAV_AH_LON 1518423250 6 -1 1 NAV_DLL_ACT 0 6 +1 1 NAV_DLL_ACT 2 6 1 1 NAV_DLL_AH_T 120.000000000000000000 9 1 1 NAV_DLL_CHSK 0 6 1 1 NAV_DLL_CH_ALT 600.000000000000000000 9 @@ -155,35 +397,28 @@ 1 1 NAV_DLL_CH_LON 1518453890 6 1 1 NAV_DLL_CH_T 120.000000000000000000 9 1 1 NAV_DLL_N 2 6 +1 1 NAV_FORCE_VT 1 6 1 1 NAV_FT_DST 8.000000000000000000 9 1 1 NAV_FT_FS 1 6 1 1 NAV_FT_RS 0.500000000000000000 9 1 1 NAV_FW_ALT_RAD 10.000000000000000000 9 -1 1 NAV_GPSF_LT 30.000000000000000000 9 +1 1 NAV_GPSF_LT 0.000000000000000000 9 1 1 NAV_GPSF_P 0.000000000000000000 9 1 1 NAV_GPSF_R 15.000000000000000000 9 -1 1 NAV_GPSF_TR 0.699999988079071045 9 -1 1 NAV_LOITER_RAD 50.000000000000000000 9 -1 1 NAV_MC_ALT_RAD 3.000000000000000000 9 +1 1 NAV_GPSF_TR 0.000000000000000000 9 +1 1 NAV_LOITER_RAD 80.000000000000000000 9 +1 1 NAV_MC_ALT_RAD 0.800000011920928955 9 1 1 NAV_MIN_FT_HT 8.000000000000000000 9 1 1 NAV_RCL_ACT 2 6 1 1 NAV_RCL_LT 120.000000000000000000 9 -1 1 PWM_AUX_DISARMED 1000 6 +1 1 NAV_TRAFF_AVOID 1 6 +1 1 PWM_AUX_DISARMED 1500 6 1 1 PWM_AUX_MAX 2000 6 1 1 PWM_AUX_MIN 1000 6 -1 1 PWM_DISARMED 0 6 -1 1 PWM_MAIN_REV1 0 6 -1 1 PWM_MAIN_REV2 0 6 -1 1 PWM_MAIN_REV3 0 6 -1 1 PWM_MAIN_REV4 0 6 -1 1 PWM_MAIN_REV5 0 6 -1 1 PWM_MAIN_REV6 0 6 -1 1 PWM_MAIN_REV7 0 6 -1 1 PWM_MAIN_REV8 0 6 +1 1 PWM_DISARMED 900 6 1 1 PWM_MAX 2000 6 1 1 PWM_MIN 1000 6 1 1 PWM_RATE 400 6 -1 1 PWM_SBUS_MODE 0 6 1 1 RC10_DZ 0.000000000000000000 9 1 1 RC10_MAX 2000.000000000000000000 9 1 1 RC10_MIN 1000.000000000000000000 9 @@ -230,24 +465,24 @@ 1 1 RC18_REV 1.000000000000000000 9 1 1 RC18_TRIM 1500.000000000000000000 9 1 1 RC1_DZ 10.000000000000000000 9 -1 1 RC1_MAX 1900.000000000000000000 9 -1 1 RC1_MIN 1100.000000000000000000 9 -1 1 RC1_REV -1.000000000000000000 9 -1 1 RC1_TRIM 1492.000000000000000000 9 +1 1 RC1_MAX 2000.000000000000000000 9 +1 1 RC1_MIN 1000.000000000000000000 9 +1 1 RC1_REV 1.000000000000000000 9 +1 1 RC1_TRIM 1500.000000000000000000 9 1 1 RC2_DZ 10.000000000000000000 9 -1 1 RC2_MAX 1900.000000000000000000 9 -1 1 RC2_MIN 1100.000000000000000000 9 +1 1 RC2_MAX 2000.000000000000000000 9 +1 1 RC2_MIN 1000.000000000000000000 9 1 1 RC2_REV 1.000000000000000000 9 -1 1 RC2_TRIM 1483.000000000000000000 9 +1 1 RC2_TRIM 1500.000000000000000000 9 1 1 RC3_DZ 10.000000000000000000 9 -1 1 RC3_MAX 1901.000000000000000000 9 -1 1 RC3_MIN 1099.000000000000000000 9 +1 1 RC3_MAX 2000.000000000000000000 9 +1 1 RC3_MIN 1000.000000000000000000 9 1 1 RC3_REV 1.000000000000000000 9 -1 1 RC3_TRIM 1099.000000000000000000 9 +1 1 RC3_TRIM 1500.000000000000000000 9 1 1 RC4_DZ 10.000000000000000000 9 -1 1 RC4_MAX 1900.000000000000000000 9 -1 1 RC4_MIN 1100.000000000000000000 9 -1 1 RC4_REV -1.000000000000000000 9 +1 1 RC4_MAX 2000.000000000000000000 9 +1 1 RC4_MIN 1000.000000000000000000 9 +1 1 RC4_REV 1.000000000000000000 9 1 1 RC4_TRIM 1500.000000000000000000 9 1 1 RC5_DZ 10.000000000000000000 9 1 1 RC5_MAX 2000.000000000000000000 9 @@ -255,8 +490,8 @@ 1 1 RC5_REV 1.000000000000000000 9 1 1 RC5_TRIM 1500.000000000000000000 9 1 1 RC6_DZ 10.000000000000000000 9 -1 1 RC6_MAX 1901.000000000000000000 9 -1 1 RC6_MIN 1099.000000000000000000 9 +1 1 RC6_MAX 2000.000000000000000000 9 +1 1 RC6_MIN 1000.000000000000000000 9 1 1 RC6_REV 1.000000000000000000 9 1 1 RC6_TRIM 1500.000000000000000000 9 1 1 RC7_DZ 10.000000000000000000 9 @@ -275,15 +510,19 @@ 1 1 RC9_REV 1.000000000000000000 9 1 1 RC9_TRIM 1500.000000000000000000 9 1 1 RC_ACRO_TH 0.500000000000000000 9 +1 1 RC_ARMSWITCH_TH 0.250000000000000000 9 1 1 RC_ASSIST_TH 0.250000000000000000 9 1 1 RC_AUTO_TH 0.750000000000000000 9 -1 1 RC_CHAN_CNT 8 6 -1 1 RC_DSM_BIND -1 6 +1 1 RC_CHAN_CNT 0 6 1 1 RC_FAILS_THR 0 6 +1 1 RC_FLT_CUTOFF 10.000000000000000000 9 +1 1 RC_FLT_SMP_RATE 50.000000000000000000 9 1 1 RC_GEAR_TH 0.250000000000000000 9 1 1 RC_KILLSWITCH_TH 0.250000000000000000 9 1 1 RC_LOITER_TH 0.500000000000000000 9 +1 1 RC_MAN_TH 0.500000000000000000 9 1 1 RC_MAP_ACRO_SW 0 6 +1 1 RC_MAP_ARM_SW 0 6 1 1 RC_MAP_AUX1 0 6 1 1 RC_MAP_AUX2 0 6 1 1 RC_MAP_AUX3 0 6 @@ -291,66 +530,210 @@ 1 1 RC_MAP_AUX5 0 6 1 1 RC_MAP_FAILSAFE 0 6 1 1 RC_MAP_FLAPS 0 6 -1 1 RC_MAP_FLTMODE 5 6 +1 1 RC_MAP_FLTMODE 0 6 1 1 RC_MAP_GEAR_SW 0 6 1 1 RC_MAP_KILL_SW 0 6 1 1 RC_MAP_LOITER_SW 0 6 +1 1 RC_MAP_MAN_SW 0 6 1 1 RC_MAP_MODE_SW 0 6 1 1 RC_MAP_OFFB_SW 0 6 1 1 RC_MAP_PARAM1 0 6 1 1 RC_MAP_PARAM2 0 6 1 1 RC_MAP_PARAM3 0 6 -1 1 RC_MAP_PITCH 2 6 +1 1 RC_MAP_PITCH 0 6 1 1 RC_MAP_POSCTL_SW 0 6 1 1 RC_MAP_RATT_SW 0 6 1 1 RC_MAP_RETURN_SW 0 6 -1 1 RC_MAP_ROLL 1 6 +1 1 RC_MAP_ROLL 0 6 +1 1 RC_MAP_STAB_SW 0 6 1 1 RC_MAP_THROTTLE 3 6 1 1 RC_MAP_TRANS_SW 0 6 -1 1 RC_MAP_YAW 4 6 +1 1 RC_MAP_YAW 0 6 1 1 RC_OFFB_TH 0.500000000000000000 9 1 1 RC_POSCTL_TH 0.500000000000000000 9 1 1 RC_RATT_TH 0.500000000000000000 9 1 1 RC_RETURN_TH 0.500000000000000000 9 -1 1 RC_RSSI_PWM_CHAN 0 6 -1 1 RC_RSSI_PWM_MAX 1000 6 -1 1 RC_RSSI_PWM_MIN 2000 6 -1 1 RC_TH_USER 1 6 +1 1 RC_STAB_TH 0.500000000000000000 9 1 1 RC_TRANS_TH 0.250000000000000000 9 -1 1 RTL_DESCEND_ALT 30.000000000000000000 9 -1 1 RTL_LAND_DELAY -1.000000000000000000 9 +1 1 RTL_DESCEND_ALT 10.000000000000000000 9 +1 1 RTL_LAND_DELAY 0.000000000000000000 9 1 1 RTL_MIN_DIST 5.000000000000000000 9 -1 1 RTL_RETURN_ALT 60.000000000000000000 9 -1 1 SDLOG_EXT -1 6 -1 1 SDLOG_GPSTIME 1 6 -1 1 SDLOG_PRIO_BOOST 2 6 -1 1 SDLOG_RATE -1 6 +1 1 RTL_RETURN_ALT 30.000000000000000000 9 +1 1 SDLOG_DIRS_MAX 0 6 +1 1 SDLOG_PROFILE 3 6 +1 1 SDLOG_UTC_OFFSET 0 6 1 1 SENS_BARO_QNH 1013.250000000000000000 9 1 1 SENS_BOARD_ROT 0 6 -1 1 SENS_BOARD_X_OFF 0.000000000000000000 9 +1 1 SENS_BOARD_X_OFF 0.000000999999997475 9 1 1 SENS_BOARD_Y_OFF 0.000000000000000000 9 1 1 SENS_BOARD_Z_OFF 0.000000000000000000 9 1 1 SENS_DPRES_ANSC 0.000000000000000000 9 -1 1 SENS_DPRES_OFF 0.000000000000000000 9 -1 1 SENS_EN_LL40LS 0 6 -1 1 SENS_EN_MB12XX 0 6 -1 1 SENS_EN_SF0X 0 6 -1 1 SENS_EN_SF1XX 0 6 -1 1 SENS_EN_THERMAL -1 6 -1 1 SENS_EN_TRONE 0 6 -1 1 SYS_AUTOCONFIG 1 6 -1 1 SYS_AUTOSTART 10018 6 -1 1 SYS_COMPANION 157600 6 -1 1 SYS_LOGGER 0 6 +1 1 SENS_DPRES_OFF 0.001000000047497451 9 +1 1 SIM_BAT_DRAIN 60.000000000000000000 9 +1 1 SITL_UDP_PRT 14560 6 +1 1 SYS_AUTOCONFIG 0 6 +1 1 SYS_AUTOSTART 6001 6 +1 1 SYS_CAL_TDEL 24 6 +1 1 SYS_CAL_TMAX 10 6 +1 1 SYS_CAL_TMIN 5 6 +1 1 SYS_HITL 0 6 1 1 SYS_MC_EST_GROUP 2 6 1 1 SYS_PARAM_VER 1 6 -1 1 SYS_RESTART_TYPE 0 6 -1 1 SYS_USE_IO 1 6 -1 1 TRIG_MODE 0 6 -1 1 TRIM_PITCH 0.000000000000000000 9 -1 1 TRIM_ROLL 0.000000000000000000 9 -1 1 TRIM_YAW 0.000000000000000000 9 -1 1 UAVCAN_ENABLE 0 6 -1 1 VT_NAV_FORCE_VT 1 6 -1 1 VT_WV_LND_EN 0 6 -1 1 VT_WV_LTR_EN 0 6 +1 1 SYS_RESTART_TYPE 2 6 +1 1 TC_A0_ID 0 6 +1 1 TC_A0_SCL_0 1.000000000000000000 9 +1 1 TC_A0_SCL_1 1.000000000000000000 9 +1 1 TC_A0_SCL_2 1.000000000000000000 9 +1 1 TC_A0_TMAX 100.000000000000000000 9 +1 1 TC_A0_TMIN 0.000000000000000000 9 +1 1 TC_A0_TREF 25.000000000000000000 9 +1 1 TC_A0_X0_0 0.000000000000000000 9 +1 1 TC_A0_X0_1 0.000000000000000000 9 +1 1 TC_A0_X0_2 0.000000000000000000 9 +1 1 TC_A0_X1_0 0.000000000000000000 9 +1 1 TC_A0_X1_1 0.000000000000000000 9 +1 1 TC_A0_X1_2 0.000000000000000000 9 +1 1 TC_A0_X2_0 0.000000000000000000 9 +1 1 TC_A0_X2_1 0.000000000000000000 9 +1 1 TC_A0_X2_2 0.000000000000000000 9 +1 1 TC_A0_X3_0 0.000000000000000000 9 +1 1 TC_A0_X3_1 0.000000000000000000 9 +1 1 TC_A0_X3_2 0.000000000000000000 9 +1 1 TC_A1_ID 0 6 +1 1 TC_A1_SCL_0 1.000000000000000000 9 +1 1 TC_A1_SCL_1 1.000000000000000000 9 +1 1 TC_A1_SCL_2 1.000000000000000000 9 +1 1 TC_A1_TMAX 100.000000000000000000 9 +1 1 TC_A1_TMIN 0.000000000000000000 9 +1 1 TC_A1_TREF 25.000000000000000000 9 +1 1 TC_A1_X0_0 0.000000000000000000 9 +1 1 TC_A1_X0_1 0.000000000000000000 9 +1 1 TC_A1_X0_2 0.000000000000000000 9 +1 1 TC_A1_X1_0 0.000000000000000000 9 +1 1 TC_A1_X1_1 0.000000000000000000 9 +1 1 TC_A1_X1_2 0.000000000000000000 9 +1 1 TC_A1_X2_0 0.000000000000000000 9 +1 1 TC_A1_X2_1 0.000000000000000000 9 +1 1 TC_A1_X2_2 0.000000000000000000 9 +1 1 TC_A1_X3_0 0.000000000000000000 9 +1 1 TC_A1_X3_1 0.000000000000000000 9 +1 1 TC_A1_X3_2 0.000000000000000000 9 +1 1 TC_A2_ID 0 6 +1 1 TC_A2_SCL_0 1.000000000000000000 9 +1 1 TC_A2_SCL_1 1.000000000000000000 9 +1 1 TC_A2_SCL_2 1.000000000000000000 9 +1 1 TC_A2_TMAX 100.000000000000000000 9 +1 1 TC_A2_TMIN 0.000000000000000000 9 +1 1 TC_A2_TREF 25.000000000000000000 9 +1 1 TC_A2_X0_0 0.000000000000000000 9 +1 1 TC_A2_X0_1 0.000000000000000000 9 +1 1 TC_A2_X0_2 0.000000000000000000 9 +1 1 TC_A2_X1_0 0.000000000000000000 9 +1 1 TC_A2_X1_1 0.000000000000000000 9 +1 1 TC_A2_X1_2 0.000000000000000000 9 +1 1 TC_A2_X2_0 0.000000000000000000 9 +1 1 TC_A2_X2_1 0.000000000000000000 9 +1 1 TC_A2_X2_2 0.000000000000000000 9 +1 1 TC_A2_X3_0 0.000000000000000000 9 +1 1 TC_A2_X3_1 0.000000000000000000 9 +1 1 TC_A2_X3_2 0.000000000000000000 9 +1 1 TC_A_ENABLE 0 6 +1 1 TC_B0_ID 0 6 +1 1 TC_B0_SCL 1.000000000000000000 9 +1 1 TC_B0_TMAX 75.000000000000000000 9 +1 1 TC_B0_TMIN 5.000000000000000000 9 +1 1 TC_B0_TREF 40.000000000000000000 9 +1 1 TC_B0_X0 0.000000000000000000 9 +1 1 TC_B0_X1 0.000000000000000000 9 +1 1 TC_B0_X2 0.000000000000000000 9 +1 1 TC_B0_X3 0.000000000000000000 9 +1 1 TC_B0_X4 0.000000000000000000 9 +1 1 TC_B0_X5 0.000000000000000000 9 +1 1 TC_B1_ID 0 6 +1 1 TC_B1_SCL 1.000000000000000000 9 +1 1 TC_B1_TMAX 75.000000000000000000 9 +1 1 TC_B1_TMIN 5.000000000000000000 9 +1 1 TC_B1_TREF 40.000000000000000000 9 +1 1 TC_B1_X0 0.000000000000000000 9 +1 1 TC_B1_X1 0.000000000000000000 9 +1 1 TC_B1_X2 0.000000000000000000 9 +1 1 TC_B1_X3 0.000000000000000000 9 +1 1 TC_B1_X4 0.000000000000000000 9 +1 1 TC_B1_X5 0.000000000000000000 9 +1 1 TC_B2_ID 0 6 +1 1 TC_B2_SCL 1.000000000000000000 9 +1 1 TC_B2_TMAX 75.000000000000000000 9 +1 1 TC_B2_TMIN 5.000000000000000000 9 +1 1 TC_B2_TREF 40.000000000000000000 9 +1 1 TC_B2_X0 0.000000000000000000 9 +1 1 TC_B2_X1 0.000000000000000000 9 +1 1 TC_B2_X2 0.000000000000000000 9 +1 1 TC_B2_X3 0.000000000000000000 9 +1 1 TC_B2_X4 0.000000000000000000 9 +1 1 TC_B2_X5 0.000000000000000000 9 +1 1 TC_B_ENABLE 0 6 +1 1 TC_G0_ID 0 6 +1 1 TC_G0_SCL_0 1.000000000000000000 9 +1 1 TC_G0_SCL_1 1.000000000000000000 9 +1 1 TC_G0_SCL_2 1.000000000000000000 9 +1 1 TC_G0_TMAX 100.000000000000000000 9 +1 1 TC_G0_TMIN 0.000000000000000000 9 +1 1 TC_G0_TREF 25.000000000000000000 9 +1 1 TC_G0_X0_0 0.000000000000000000 9 +1 1 TC_G0_X0_1 0.000000000000000000 9 +1 1 TC_G0_X0_2 0.000000000000000000 9 +1 1 TC_G0_X1_0 0.000000000000000000 9 +1 1 TC_G0_X1_1 0.000000000000000000 9 +1 1 TC_G0_X1_2 0.000000000000000000 9 +1 1 TC_G0_X2_0 0.000000000000000000 9 +1 1 TC_G0_X2_1 0.000000000000000000 9 +1 1 TC_G0_X2_2 0.000000000000000000 9 +1 1 TC_G0_X3_0 0.000000000000000000 9 +1 1 TC_G0_X3_1 0.000000000000000000 9 +1 1 TC_G0_X3_2 0.000000000000000000 9 +1 1 TC_G1_ID 0 6 +1 1 TC_G1_SCL_0 1.000000000000000000 9 +1 1 TC_G1_SCL_1 1.000000000000000000 9 +1 1 TC_G1_SCL_2 1.000000000000000000 9 +1 1 TC_G1_TMAX 100.000000000000000000 9 +1 1 TC_G1_TMIN 0.000000000000000000 9 +1 1 TC_G1_TREF 25.000000000000000000 9 +1 1 TC_G1_X0_0 0.000000000000000000 9 +1 1 TC_G1_X0_1 0.000000000000000000 9 +1 1 TC_G1_X0_2 0.000000000000000000 9 +1 1 TC_G1_X1_0 0.000000000000000000 9 +1 1 TC_G1_X1_1 0.000000000000000000 9 +1 1 TC_G1_X1_2 0.000000000000000000 9 +1 1 TC_G1_X2_0 0.000000000000000000 9 +1 1 TC_G1_X2_1 0.000000000000000000 9 +1 1 TC_G1_X2_2 0.000000000000000000 9 +1 1 TC_G1_X3_0 0.000000000000000000 9 +1 1 TC_G1_X3_1 0.000000000000000000 9 +1 1 TC_G1_X3_2 0.000000000000000000 9 +1 1 TC_G2_ID 0 6 +1 1 TC_G2_SCL_0 1.000000000000000000 9 +1 1 TC_G2_SCL_1 1.000000000000000000 9 +1 1 TC_G2_SCL_2 1.000000000000000000 9 +1 1 TC_G2_TMAX 100.000000000000000000 9 +1 1 TC_G2_TMIN 0.000000000000000000 9 +1 1 TC_G2_TREF 25.000000000000000000 9 +1 1 TC_G2_X0_0 0.000000000000000000 9 +1 1 TC_G2_X0_1 0.000000000000000000 9 +1 1 TC_G2_X0_2 0.000000000000000000 9 +1 1 TC_G2_X1_0 0.000000000000000000 9 +1 1 TC_G2_X1_1 0.000000000000000000 9 +1 1 TC_G2_X1_2 0.000000000000000000 9 +1 1 TC_G2_X2_0 0.000000000000000000 9 +1 1 TC_G2_X2_1 0.000000000000000000 9 +1 1 TC_G2_X2_2 0.000000000000000000 9 +1 1 TC_G2_X3_0 0.000000000000000000 9 +1 1 TC_G2_X3_1 0.000000000000000000 9 +1 1 TC_G2_X3_2 0.000000000000000000 9 +1 1 TC_G_ENABLE 0 6 +1 1 TRIG_ACT_TIME 40.000000000000000000 9 +1 1 TRIG_DISTANCE 25.000000000000000000 9 +1 1 TRIG_INTERFACE 3 6 +1 1 TRIG_INTERVAL 40.000000000000000000 9 +1 1 TRIG_MODE 4 6 +1 1 VT_B_DEC_MSS 2.000000000000000000 9 +1 1 VT_B_REV_DEL 0.000000000000000000 9 diff --git a/src/comm/QGCSerialPortInfo.cc b/src/comm/QGCSerialPortInfo.cc index 1f07da7d2c152043b9ae16aef6f05584d804bfc0..5e3c51331e7759331d22f4758f0fa47df387ab0a 100644 --- a/src/comm/QGCSerialPortInfo.cc +++ b/src/comm/QGCSerialPortInfo.cc @@ -252,7 +252,9 @@ QList QGCSerialPortInfo::availablePorts(void) QList list; foreach(QSerialPortInfo portInfo, QSerialPortInfo::availablePorts()) { - list << *((QGCSerialPortInfo*)&portInfo); + if (!isSystemPort(&portInfo)) { + list << *((QGCSerialPortInfo*)&portInfo); + } } return list; @@ -271,6 +273,28 @@ bool QGCSerialPortInfo::isBootloader(void) const } } +bool QGCSerialPortInfo::isSystemPort(QSerialPortInfo* port) +{ + // Known operating system peripherals that are NEVER a peripheral + // that we should connect to. + + // XXX Add Linux (LTE modems, etc) and Windows as needed + + // MAC OS + if (port->systemLocation().contains("tty.MALS") + || port->systemLocation().contains("tty.SOC") + || port->systemLocation().contains("tty.Bluetooth-Incoming-Port") + // We open these by their cu.usbserial and cu.usbmodem handles + // already. We don't want to open them twice and conflict + // with ourselves. + || port->systemLocation().contains("tty.usbserial") + || port->systemLocation().contains("tty.usbmodem")) { + + return true; + } + return false; +} + bool QGCSerialPortInfo::canFlash(void) { BoardType_t boardType; diff --git a/src/comm/QGCSerialPortInfo.h b/src/comm/QGCSerialPortInfo.h index 0148da2f0747de614859a00fe69f5c7f42a91085..6937ac728f314fa78e8022ec72600e9e03436dd2 100644 --- a/src/comm/QGCSerialPortInfo.h +++ b/src/comm/QGCSerialPortInfo.h @@ -49,6 +49,9 @@ public: /// @return true: Board is currently in bootloader bool isBootloader(void) const; + /// @return true: Port is a system port and not an autopilot + static bool isSystemPort(QSerialPortInfo* port); + private: typedef struct { const char* classString; diff --git a/src/comm/SerialLink.cc b/src/comm/SerialLink.cc index c48690cb755ad1ec29245cf99ab62793c82f6bdf..3ae34a3e99ed37f21fe33b9801644f2d8ec85f12 100644 --- a/src/comm/SerialLink.cc +++ b/src/comm/SerialLink.cc @@ -157,7 +157,12 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& if (_port) { qCDebug(SerialLinkLog) << "SerialLink:" << QString::number((long)this, 16) << "closing port"; _port->close(); - QGC::SLEEP::usleep(50000); + + // Wait 50 ms while continuing to run the event queue + for (unsigned i = 0; i < 10; i++) { + QGC::SLEEP::usleep(5000); + qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); + } delete _port; _port = NULL; } @@ -169,12 +174,22 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& qCDebug(SerialLinkLog) << "Not connecting to a bootloader, waiting for 2nd chance"; const unsigned retry_limit = 12; unsigned retries; + for (retries = 0; retries < retry_limit; retries++) { if (!_isBootloader()) { - QGC::SLEEP::msleep(500); + // Wait 500 ms while continuing to run the event loop + for (unsigned i = 0; i < 100; i++) { + QGC::SLEEP::msleep(5); + qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); + } break; } - QGC::SLEEP::msleep(500); + + // Wait 500 ms while continuing to run the event loop + for (unsigned i = 0; i < 100; i++) { + QGC::SLEEP::msleep(5); + qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); + } } // Check limit if (retries == retry_limit) { @@ -199,10 +214,17 @@ bool SerialLink::_hardwareConnect(QSerialPort::SerialPortError& error, QString& #ifdef __android__ _port->open(QIODevice::ReadWrite); #else - for (int openRetries = 0; openRetries < 4; openRetries++) { + + // Try to open the port three times + for (int openRetries = 0; openRetries < 3; openRetries++) { if (!_port->open(QIODevice::ReadWrite)) { qCDebug(SerialLinkLog) << "Port open failed, retrying"; - QGC::SLEEP::msleep(500); + // Wait 250 ms while continuing to run the event loop + for (unsigned i = 0; i < 50; i++) { + QGC::SLEEP::msleep(5); + qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); + } + qgcApp()->processEvents(QEventLoop::ExcludeUserInputEvents); } else { break; } diff --git a/src/comm/SerialLink.h b/src/comm/SerialLink.h index a70e27a30c74542386c668bf6caf697e900f54f0..9430f20686586fd8193b571ef6b67db5944f3dac 100644 --- a/src/comm/SerialLink.h +++ b/src/comm/SerialLink.h @@ -86,6 +86,7 @@ public: /// From LinkConfiguration LinkType type () { return LinkConfiguration::TypeSerial; } void copyFrom (LinkConfiguration* source); + bool isHighLatencyAllowed () { return true; } void loadSettings (QSettings& settings, const QString& root); void saveSettings (QSettings& settings, const QString& root); void updateSettings (); diff --git a/src/comm/TCPLink.h b/src/comm/TCPLink.h index 9eb2827feb43a947f8ccec9e975e9dff23017050..907af9cbc5dfe0aa2ee6d479a114b066ce97826f 100644 --- a/src/comm/TCPLink.h +++ b/src/comm/TCPLink.h @@ -97,6 +97,7 @@ public: /// From LinkConfiguration LinkType type () { return LinkConfiguration::TypeTcp; } void copyFrom (LinkConfiguration* source); + bool isHighLatencyAllowed () { return true; } void loadSettings (QSettings& settings, const QString& root); void saveSettings (QSettings& settings, const QString& root); void updateSettings (); diff --git a/src/comm/UDPLink.cc b/src/comm/UDPLink.cc index ab91418ec115bb0d36436247a18d6c42639c1d17..ea80e3edb9e3cee95712775d40536a87ea5cf11d 100644 --- a/src/comm/UDPLink.cc +++ b/src/comm/UDPLink.cc @@ -68,6 +68,37 @@ static QString get_ip_address(const QString& address) return QString(""); } +static bool is_ip_local(const QHostAddress& add) +{ + // In simulation and testing setups the vehicle and the GCS can be + // running on the same host. This leads to packets arriving through + // the local network or the loopback adapter, which makes it look + // like the vehicle is connected through two different links, + // complicating routing. + // + // We detect this case and force all traffic to a simulated instance + // onto the local loopback interface. + // Run through all IPv4 interfaces and check if their canonical + // IP address in string representation matches the source IP address + foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) { + if (address == add) { + // This is a local address of the same host + return true; + } + } + return false; +} + +static bool contains_target(const QList list, const QHostAddress& address, quint16 port) +{ + foreach(UDPCLient* target, list) { + if(target->address == address && target->port == port) { + return true; + } + } + return false; +} + UDPLink::UDPLink(SharedLinkConfigurationPointer& config) : LinkInterface(config) #if defined(QGC_ZEROCONF_ENABLED) @@ -89,6 +120,9 @@ UDPLink::~UDPLink() _disconnect(); // Tell the thread to exit _running = false; + // Clear client list + qDeleteAll(_sessionTargets); + _sessionTargets.clear(); quit(); // Wait for it to exit wait(); @@ -124,50 +158,34 @@ QString UDPLink::getName() const return _udpConfig->name(); } -void UDPLink::addHost(const QString& host) -{ - _udpConfig->addHost(host); -} - -void UDPLink::removeHost(const QString& host) -{ - _udpConfig->removeHost(host); -} - void UDPLink::_writeBytes(const QByteArray data) { if (!_socket) return; - - QStringList goneHosts; - // Send to all connected systems - QString host; - int port; - if(_udpConfig->firstHost(host, port)) { - do { - QHostAddress currentHost(host); - if(_socket->writeDatagram(data, currentHost, (quint16)port) < 0) { - // This host is gone. Add to list to be removed - // We should keep track of hosts that were manually added (static) and - // hosts that were added because we heard from them (dynamic). Only - // dynamic hosts should be removed and even then, after a few tries, not - // the first failure. In the mean time, we don't remove anything. - if(REMOVE_GONE_HOSTS) { - goneHosts.append(host); - } - } else { - // Only log rate if data actually got sent. Not sure about this as - // "host not there" takes time too regardless of size of data. In fact, - // 1 byte or "UDP frame size" bytes are the same as that's the data - // unit sent by UDP. - _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); - } - } while (_udpConfig->nextHost(host, port)); - //-- Remove hosts that are no longer there - foreach (const QString& ghost, goneHosts) { - _udpConfig->removeHost(ghost); + // Send to all manually targeted systems + foreach(UDPCLient* target, _udpConfig->targetHosts()) { + // Skip it if it's part of the session clients below + if(!contains_target(_sessionTargets, target->address, target->port)) { + _writeDataGram(data, target); } } + // Send to all connected systems + foreach(UDPCLient* target, _sessionTargets) { + _writeDataGram(data, target); + } +} + +void UDPLink::_writeDataGram(const QByteArray data, const UDPCLient* target) +{ + if(_socket->writeDatagram(data, target->address, target->port) < 0) { + qWarning() << "Error writing to" << target->address << target->port; + } else { + // Only log rate if data actually got sent. Not sure about this as + // "host not there" takes time too regardless of size of data. In fact, + // 1 byte or "UDP frame size" bytes are the same as that's the data + // unit sent by UDP. + _logOutputDataRate(data.size(), QDateTime::currentMSecsSinceEpoch()); + } } /** @@ -190,11 +208,19 @@ void UDPLink::readBytes() databuffer.clear(); } _logInputDataRate(datagram.length(), QDateTime::currentMSecsSinceEpoch()); - // TODO This doesn't validade the sender. Anything sending UDP packets to this port gets + // TODO: This doesn't validade the sender. Anything sending UDP packets to this port gets // added to the list and will start receiving datagrams from here. Even a port scanner // would trigger this. // Add host to broadcast list if not yet present, or update its port - _udpConfig->addHost(sender.toString(), (int)senderPort); + QHostAddress asender = sender; + if(is_ip_local(sender)) { + asender = QHostAddress(QString("127.0.0.1")); + } + if(!contains_target(_sessionTargets, asender, senderPort)) { + qDebug() << "Adding target" << asender << senderPort; + UDPCLient* target = new UDPCLient(asender, senderPort); + _sessionTargets.append(target); + } } //-- Send whatever is left if(databuffer.size()) { @@ -336,42 +362,49 @@ UDPConfiguration::UDPConfiguration(const QString& name) : LinkConfiguration(name _localPort = settings->udpListenPort()->rawValue().toInt(); QString targetHostIP = settings->udpTargetHostIP()->rawValue().toString(); if (!targetHostIP.isEmpty()) { - addHost(targetHostIP, settings->udpTargetHostPort()->rawValue().toInt()); + addHost(targetHostIP, settings->udpTargetHostPort()->rawValue().toUInt()); } } UDPConfiguration::UDPConfiguration(UDPConfiguration* source) : LinkConfiguration(source) { - _localPort = source->localPort(); - QString host; - int port; - _hostList.clear(); - if(source->firstHost(host, port)) { - do { - addHost(host, port); - } while(source->nextHost(host, port)); - } + _copyFrom(source); +} + +UDPConfiguration::~UDPConfiguration() +{ + _clearTargetHosts(); } void UDPConfiguration::copyFrom(LinkConfiguration *source) { LinkConfiguration::copyFrom(source); + _copyFrom(source); +} + +void UDPConfiguration::_copyFrom(LinkConfiguration *source) +{ UDPConfiguration* usource = dynamic_cast(source); if (usource) { _localPort = usource->localPort(); - _hosts.clear(); - QString host; - int port; - if(usource->firstHost(host, port)) { - do { - addHost(host, port); - } while(usource->nextHost(host, port)); + _clearTargetHosts(); + foreach(UDPCLient* target, usource->targetHosts()) { + if(!contains_target(_targetHosts, target->address, target->port)) { + UDPCLient* newTarget = new UDPCLient(target); + _targetHosts.append(newTarget); + } } } else { qWarning() << "Internal error"; } } +void UDPConfiguration::_clearTargetHosts() +{ + qDeleteAll(_targetHosts); + _targetHosts.clear(); +} + /** * @param host Hostname in standard formatt, e.g. localhost:14551 or 192.168.1.1:14551 */ @@ -380,106 +413,50 @@ void UDPConfiguration::addHost(const QString host) // Handle x.x.x.x:p if (host.contains(":")) { - addHost(host.split(":").first(), host.split(":").last().toInt()); + addHost(host.split(":").first(), host.split(":").last().toUInt()); } // If no port, use default else { - addHost(host, (int)_localPort); + addHost(host, _localPort); } } -void UDPConfiguration::addHost(const QString& host, int port) +void UDPConfiguration::addHost(const QString& host, quint16 port) { - bool changed = false; - QMutexLocker locker(&_confMutex); - if(_hosts.contains(host)) { - if(_hosts[host] != port) { - _hosts[host] = port; - changed = true; - } + QString ipAdd = get_ip_address(host); + if(ipAdd.isEmpty()) { + qWarning() << "UDP:" << "Could not resolve host:" << host << "port:" << port; } else { - QString ipAdd = get_ip_address(host); - if(ipAdd.isEmpty()) { - qWarning() << "UDP:" << "Could not resolve host:" << host << "port:" << port; - } else { - // In simulation and testing setups the vehicle and the GCS can be - // running on the same host. This leads to packets arriving through - // the local network or the loopback adapter, which makes it look - // like the vehicle is connected through two different links, - // complicating routing. - // - // We detect this case and force all traffic to a simulated instance - // onto the local loopback interface. - bool not_local = true; - // Run through all IPv4 interfaces and check if their canonical - // IP address in string representation matches the source IP address - foreach (const QHostAddress &address, QNetworkInterface::allAddresses()) { - if (address.protocol() == QAbstractSocket::IPv4Protocol) { - if (ipAdd.endsWith(address.toString())) { - // This is a local address of the same host - not_local = false; - } - } - } - if (not_local) { - // This is a normal remote host, add it using its IPv4 address - _hosts[ipAdd] = port; - //qDebug() << "UDP:" << "Adding Host:" << ipAdd << ":" << port; - } else { - // It is localhost, so talk to it through the IPv4 loopback interface - _hosts["127.0.0.1"] = port; - } - changed = true; + QHostAddress address(ipAdd); + if(!contains_target(_targetHosts, address, port)) { + UDPCLient* newTarget = new UDPCLient(address, port); + _targetHosts.append(newTarget); + _updateHostList(); } } - if(changed) { - _updateHostList(); - } } void UDPConfiguration::removeHost(const QString host) { - QMutexLocker locker(&_confMutex); - QString tHost = host; - if (tHost.contains(":")) { - tHost = tHost.split(":").first(); - } - tHost = tHost.trimmed(); - QMap::iterator i = _hosts.find(tHost); - if(i != _hosts.end()) { - //qDebug() << "UDP:" << "Removed host:" << host; - _hosts.erase(i); - } else { - qWarning() << "UDP:" << "Could not remove unknown host:" << host; + if (host.contains(":")) + { + QHostAddress address = QHostAddress(get_ip_address(host.split(":").first())); + quint16 port = host.split(":").last().toUInt(); + for(int i = 0; i < _targetHosts.size(); i++) { + UDPCLient* target = _targetHosts.at(i); + if(target->address == address && target->port == port) { + _targetHosts.removeAt(i); + delete target; + _updateHostList(); + return; + } + } } + qWarning() << "UDP:" << "Could not remove unknown host:" << host; _updateHostList(); } -bool UDPConfiguration::firstHost(QString& host, int& port) -{ - _confMutex.lock(); - _it = _hosts.begin(); - if(_it == _hosts.end()) { - _confMutex.unlock(); - return false; - } - _confMutex.unlock(); - return nextHost(host, port); -} - -bool UDPConfiguration::nextHost(QString& host, int& port) -{ - QMutexLocker locker(&_confMutex); - if(_it != _hosts.end()) { - host = _it.key(); - port = _it.value(); - _it++; - return true; - } - return false; -} - void UDPConfiguration::setLocalPort(quint16 port) { _localPort = port; @@ -487,31 +464,23 @@ void UDPConfiguration::setLocalPort(quint16 port) void UDPConfiguration::saveSettings(QSettings& settings, const QString& root) { - _confMutex.lock(); settings.beginGroup(root); settings.setValue("port", (int)_localPort); - settings.setValue("hostCount", _hosts.count()); - int index = 0; - QMap::const_iterator it = _hosts.begin(); - while(it != _hosts.end()) { - QString hkey = QString("host%1").arg(index); - settings.setValue(hkey, it.key()); - QString pkey = QString("port%1").arg(index); - settings.setValue(pkey, it.value()); - it++; - index++; + settings.setValue("hostCount", _targetHosts.size()); + for(int i = 0; i < _targetHosts.size(); i++) { + UDPCLient* target = _targetHosts.at(i); + QString hkey = QString("host%1").arg(i); + settings.setValue(hkey, target->address.toString()); + QString pkey = QString("port%1").arg(i); + settings.setValue(pkey, target->port); } settings.endGroup(); - _confMutex.unlock(); } void UDPConfiguration::loadSettings(QSettings& settings, const QString& root) { AutoConnectSettings* acSettings = qgcApp()->toolbox()->settingsManager()->autoConnectSettings(); - - _confMutex.lock(); - _hosts.clear(); - _confMutex.unlock(); + _clearTargetHosts(); settings.beginGroup(root); _localPort = (quint16)settings.value("port", acSettings->udpListenPort()->rawValue().toInt()).toUInt(); int hostCount = settings.value("hostCount", 0).toInt(); @@ -519,7 +488,7 @@ void UDPConfiguration::loadSettings(QSettings& settings, const QString& root) QString hkey = QString("host%1").arg(i); QString pkey = QString("port%1").arg(i); if(settings.contains(hkey) && settings.contains(pkey)) { - addHost(settings.value(hkey).toString(), settings.value(pkey).toInt()); + addHost(settings.value(hkey).toString(), settings.value(pkey).toUInt()); } } settings.endGroup(); @@ -539,11 +508,10 @@ void UDPConfiguration::updateSettings() void UDPConfiguration::_updateHostList() { _hostList.clear(); - QMap::const_iterator it = _hosts.begin(); - while(it != _hosts.end()) { - QString host = QString("%1").arg(it.key()) + ":" + QString("%1").arg(it.value()); - _hostList += host; - it++; + for(int i = 0; i < _targetHosts.size(); i++) { + UDPCLient* target = _targetHosts.at(i); + QString host = QString("%1").arg(target->address.toString()) + ":" + QString("%1").arg(target->port); + _hostList << host; } emit hostListChanged(); } diff --git a/src/comm/UDPLink.h b/src/comm/UDPLink.h index 6bdf5da845e7966049adf9db5037c940c7ccf073..6e12c6af7bdef0ad4b2a951cc1ee2f5d1ef94e4e 100644 --- a/src/comm/UDPLink.h +++ b/src/comm/UDPLink.h @@ -34,10 +34,23 @@ #include "QGCConfig.h" #include "LinkManager.h" +class UDPCLient { +public: + UDPCLient(const QHostAddress& address_, quint16 port_) + : address(address_) + , port(port_) + {} + UDPCLient(const UDPCLient* other) + : address(other->address) + , port(other->port) + {} + QHostAddress address; + quint16 port; +}; + class UDPConfiguration : public LinkConfiguration { Q_OBJECT - public: Q_PROPERTY(quint16 localPort READ localPort WRITE setLocalPort NOTIFY localPortChanged) @@ -61,30 +74,7 @@ public: */ UDPConfiguration(UDPConfiguration* source); - /*! - * @brief Begin iteration through the list of target hosts - * - * @param[out] host Host name - * @param[out] port Port number - * @return Returns false if list is empty - */ - bool firstHost (QString& host, int& port); - - /*! - * @brief Continues iteration through the list of target hosts - * - * @param[out] host Host name - * @param[out] port Port number - * @return Returns false if reached the end of the list (in which case, both host and port are unchanged) - */ - bool nextHost (QString& host, int& port); - - /*! - * @brief Get the number of target hosts - * - * @return Number of hosts in list - */ - int hostCount () { return _hosts.count(); } + ~UDPConfiguration(); /*! * @brief The UDP port we bind to @@ -106,7 +96,7 @@ public: * @param[in] host Host name, e.g. localhost or 192.168.1.1 * @param[in] port Port number */ - void addHost (const QString& host, int port); + void addHost (const QString& host, quint16 port); /*! * @brief Remove a target host from the list @@ -127,6 +117,8 @@ public: */ QStringList hostList () { return _hostList; } + const QList targetHosts() { return _targetHosts; } + /// From LinkConfiguration LinkType type () { return LinkConfiguration::TypeUdp; } void copyFrom (LinkConfiguration* source); @@ -134,6 +126,7 @@ public: void saveSettings (QSettings& settings, const QString& root); void updateSettings (); bool isAutoConnectAllowed () { return true; } + bool isHighLatencyAllowed () { return true; } QString settingsURL () { return "UdpSettings.qml"; } signals: @@ -142,13 +135,13 @@ signals: private: void _updateHostList (); + void _clearTargetHosts (); + void _copyFrom (LinkConfiguration *source); private: - QMutex _confMutex; - QMap::iterator _it; - QMap _hosts; ///< ("host", port) - QStringList _hostList; ///< Exposed to QML - quint16 _localPort; + QList _targetHosts; + QStringList _hostList; ///< Exposed to QML + quint16 _localPort; }; class UDPLink : public LinkInterface @@ -159,32 +152,28 @@ class UDPLink : public LinkInterface friend class LinkManager; public: - void requestReset() { } - bool isConnected() const; - QString getName() const; + void requestReset () override { } + bool isConnected () const override; + QString getName () const override; // Extensive statistics for scientific purposes - qint64 getConnectionSpeed() const; - qint64 getCurrentInDataRate() const; - qint64 getCurrentOutDataRate() const; + qint64 getConnectionSpeed () const override; + qint64 getCurrentInDataRate () const; + qint64 getCurrentOutDataRate () const; - void run(); + // Thread + void run () override; // These are left unimplemented in order to cause linker errors which indicate incorrect usage of // connect/disconnect on link directly. All connect/disconnect calls should be made through LinkManager. - bool connect(void); - bool disconnect(void); + bool connect (void); + bool disconnect (void); public slots: - /*! @brief Add a new host to broadcast messages to */ - void addHost (const QString& host); - /*! @brief Remove a host from broadcasting messages to */ - void removeHost (const QString& host); - - void readBytes(); + void readBytes (); private slots: - void _writeBytes(const QByteArray data); + void _writeBytes (const QByteArray data) override; private: // Links are only created/destroyed by LinkManager so constructor/destructor is not public @@ -192,14 +181,14 @@ private: ~UDPLink(); // From LinkInterface - virtual bool _connect(void); - virtual void _disconnect(void); + bool _connect (void) override; + void _disconnect (void) override; - bool _hardwareConnect(); - void _restartConnection(); - - void _registerZeroconf(uint16_t port, const std::string& regType); - void _deregisterZeroconf(); + bool _hardwareConnect (); + void _restartConnection (); + void _registerZeroconf (uint16_t port, const std::string& regType); + void _deregisterZeroconf (); + void _writeDataGram (const QByteArray data, const UDPCLient* target); #if defined(QGC_ZEROCONF_ENABLED) DNSServiceRef _dnssServiceRef; @@ -209,6 +198,8 @@ private: QUdpSocket* _socket; UDPConfiguration* _udpConfig; bool _connectState; + QList _sessionTargets; + }; #endif // UDPLINK_H diff --git a/src/qgcunittest/UnitTest.cc b/src/qgcunittest/UnitTest.cc index f2c9d260946f2cf890ae7cf0f477dede74887888..bf0f0854c8d49628a2742332b7ae2670baaf55a9 100644 --- a/src/qgcunittest/UnitTest.cc +++ b/src/qgcunittest/UnitTest.cc @@ -523,3 +523,12 @@ bool UnitTest::doubleNaNCompare(double value1, double value2) return ret; } } + +void UnitTest::changeFactValue(Fact* fact) +{ + if (fact->typeIsBool()) { + fact->setRawValue(!fact->rawValue().toBool()); + } else { + fact->setRawValue(fact->rawValue().toDouble() + 1); + } +} diff --git a/src/qgcunittest/UnitTest.h b/src/qgcunittest/UnitTest.h index fa1d164f175ebeda594d3c909d5a4326686634ef..6690284e6f262ca42d62a3538383c948a2375e3b 100644 --- a/src/qgcunittest/UnitTest.h +++ b/src/qgcunittest/UnitTest.h @@ -24,6 +24,7 @@ #include "QGCMAVLink.h" #include "LinkInterface.h" +#include "Fact.h" #define UT_REGISTER_TEST(className) static UnitTestWrapper className(#className); @@ -96,6 +97,9 @@ public: /// @return true: equal static bool doubleNaNCompare(double value1, double value2); + /// Changes the Facts rawValue such that it emits a valueChanged signal. + void changeFactValue(Fact* fact); + protected slots: // These are all pure virtuals to force the derived class to implement each one and in turn diff --git a/src/qgcunittest/UnitTestList.cc b/src/qgcunittest/UnitTestList.cc index 0fb23b5ecbee2968ab57a1c4eca63c4b6dff32a1..97f11ce3a0dfa6ed4519556ebd1701152a21a5dd 100644 --- a/src/qgcunittest/UnitTestList.cc +++ b/src/qgcunittest/UnitTestList.cc @@ -39,6 +39,11 @@ #include "MissionSettingsTest.h" #include "QGCMapPolygonTest.h" #include "AudioOutputTest.h" +#include "StructureScanComplexItemTest.h" +#include "QGCMapPolylineTest.h" +#include "CorridorScanComplexItemTest.h" +#include "TransectStyleComplexItemTest.h" +#include "CameraCalcTest.h" UT_REGISTER_TEST(FactSystemTestGeneric) UT_REGISTER_TEST(FactSystemTestPX4) @@ -65,6 +70,11 @@ UT_REGISTER_TEST(PlanMasterControllerTest) UT_REGISTER_TEST(MissionSettingsTest) UT_REGISTER_TEST(QGCMapPolygonTest) UT_REGISTER_TEST(AudioOutputTest) +UT_REGISTER_TEST(StructureScanComplexItemTest) +UT_REGISTER_TEST(CorridorScanComplexItemTest) +UT_REGISTER_TEST(TransectStyleComplexItemTest) +UT_REGISTER_TEST(QGCMapPolylineTest) +UT_REGISTER_TEST(CameraCalcTest) // List of unit test which are currently disabled. // If disabling a new test, include reason in comment. diff --git a/src/ui/MainWindow.cc b/src/ui/MainWindow.cc index 041cbe5a76304a54878b1e20c5f871f5efd2754a..457948e579289aeaade308589b500707e240ac29 100644 --- a/src/ui/MainWindow.cc +++ b/src/ui/MainWindow.cc @@ -135,6 +135,7 @@ MainWindow::MainWindow() _ui.setupUi(this); // Make sure tool bar elements all fit before changing minimum width setMinimumWidth(1008); + setMinimumHeight(520); configureWindowName(); // Setup central widget with a layout to hold the views @@ -244,21 +245,6 @@ MainWindow::MainWindow() menuBar()->hide(); #endif show(); -#ifdef __macos__ - // TODO HACK - // This is a really ugly hack. For whatever reason, by having a QQuickWidget inside a - // QDockWidget (MainToolBar above), the main menu is not shown when the app first - // starts. I looked everywhere and I could not find a solution. What I did notice was - // that if any other window gets focus, the menu comes up when you come back to QGC. - // That is, if you were to click on another window and then back to QGC, the menus - // would appear. This hack below creates a 0x0 dialog and immediately closes it. - // That works around the issue and it will do until I find the root of the problem. - QDialog qd(this); - qd.show(); - qd.raise(); - qd.activateWindow(); - qd.close(); -#endif } #ifndef __mobile__ @@ -395,7 +381,7 @@ void MainWindow::showStatusBarCallback(bool checked) checked ? statusBar()->show() : statusBar()->hide(); } -void MainWindow::reallyClose(void) +void MainWindow::_reallyClose(void) { _forceClose = true; close(); @@ -458,6 +444,7 @@ void MainWindow::connectCommonActions() { // Connect internal actions connect(qgcApp()->toolbox()->multiVehicleManager(), &MultiVehicleManager::vehicleAdded, this, &MainWindow::_vehicleAdded); + connect(this, &MainWindow::reallyClose, this, &MainWindow::_reallyClose, Qt::QueuedConnection); // Queued to allow closeEvent to fully unwind before _reallyClose is called } void MainWindow::_openUrl(const QString& url, const QString& errorMessage) diff --git a/src/ui/MainWindow.h b/src/ui/MainWindow.h index 5efc40ab718daa6dcd436883de8ae1883c73779f..001955f9eec9ee838af31266e5a5be50888126b2 100644 --- a/src/ui/MainWindow.h +++ b/src/ui/MainWindow.h @@ -80,7 +80,7 @@ public: void saveLastUsedConnection(const QString connection); // Called from MainWindow.qml when the user accepts the window close dialog - Q_INVOKABLE void reallyClose(void); + void _reallyClose(void); /// @return Root qml object of main window QML QObject* rootQmlObject(void); @@ -104,6 +104,7 @@ signals: void initStatusChanged(const QString& message, int alignment, const QColor &color); /** Emitted when any value changes from any source */ void valueChanged(const int uasId, const QString& name, const QString& unit, const QVariant& value, const quint64 msec); + void reallyClose(void); // Used for unit tests to know when the main window closes void mainWindowClosed(void); diff --git a/src/ui/MainWindowHybrid.qml b/src/ui/MainWindowHybrid.qml index 408ea37a94b60bc4808c1467f4134a3d4b20facd..8f58d2253d736dbbcfef220880797d5476b8cd6a 100644 --- a/src/ui/MainWindowHybrid.qml +++ b/src/ui/MainWindowHybrid.qml @@ -22,7 +22,12 @@ Item { } function attemptWindowClose() { - mainWindowInner.item.attemptWindowClose() + try { + mainWindowInner.item.attemptWindowClose() + } + finally { + controller.reallyClose() + } } function showMessage(message) { diff --git a/src/ui/MainWindowInner.qml b/src/ui/MainWindowInner.qml index f742ab914ce244563805b0b780d7972f574eac85..e4b6069bda104a8f54a49074eef213ca97767512 100644 --- a/src/ui/MainWindowInner.qml +++ b/src/ui/MainWindowInner.qml @@ -292,15 +292,14 @@ Item { flightView.guidedController.confirmAction(flightView.guidedController.actionDisarm) } } + onVtolTransitionToFwdFlight: flightView.guidedController.confirmAction(flightView.guidedController.actionVtolTransitionToFwdFlight) + onVtolTransitionToMRFlight: flightView.guidedController.confirmAction(flightView.guidedController.actionVtolTransitionToMRFlight) //-- Entire tool bar area disable on cammand - MouseArea { + DeadMouseArea { id: toolbarBlocker - anchors.fill: parent enabled: false - onWheel: { wheel.accepted = true; } - onPressed: { mouse.accepted = true; } - onReleased: { mouse.accepted = true; } + anchors.fill: parent } } diff --git a/src/ui/preferences/GeneralSettings.qml b/src/ui/preferences/GeneralSettings.qml index 4d6c4ae3df6d95ccec2b1dc8c66c955938fe8597..e74cfe334f5cfb5fe4d05d06a2c5604a449a5b41 100644 --- a/src/ui/preferences/GeneralSettings.qml +++ b/src/ui/preferences/GeneralSettings.qml @@ -37,10 +37,13 @@ QGCView { property Fact _appFontPointSize: QGroundControl.settingsManager.appSettings.appFontPointSize property Fact _userBrandImageIndoor: QGroundControl.settingsManager.brandImageSettings.userBrandImageIndoor property Fact _userBrandImageOutdoor: QGroundControl.settingsManager.brandImageSettings.userBrandImageOutdoor - property real _labelWidth: ScreenTools.defaultFontPixelWidth * 15 + property real _labelWidth: ScreenTools.defaultFontPixelWidth * 20 property real _editFieldWidth: ScreenTools.defaultFontPixelWidth * 30 property Fact _mapProvider: QGroundControl.settingsManager.flightMapSettings.mapProvider property Fact _mapType: QGroundControl.settingsManager.flightMapSettings.mapType + property real _panelWidth: _qgcView.width * _internalWidthRatio + + readonly property real _internalWidthRatio: 0.8 readonly property string _requiresRestart: qsTr("(Requires Restart)") @@ -63,7 +66,7 @@ QGCView { //----------------------------------------------------------------- //-- Units Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: unitLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -76,7 +79,7 @@ QGCView { } Rectangle { height: unitsCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -88,9 +91,9 @@ QGCView { Repeater { id: unitsRepeater - model: [ QGroundControl.settingsManager.unitsSettings.distanceUnits, QGroundControl.settingsManager.unitsSettings.areaUnits, QGroundControl.settingsManager.unitsSettings.speedUnits ] + model: [ QGroundControl.settingsManager.unitsSettings.distanceUnits, QGroundControl.settingsManager.unitsSettings.areaUnits, QGroundControl.settingsManager.unitsSettings.speedUnits, QGroundControl.settingsManager.unitsSettings.temperatureUnits ] - property var names: [ qsTr("Distance:"), qsTr("Area:"), qsTr("Speed:") ] + property var names: [ qsTr("Distance:"), qsTr("Area:"), qsTr("Speed:"), qsTr("Temperature:") ] Row { spacing: ScreenTools.defaultFontPixelWidth @@ -115,7 +118,7 @@ QGCView { //----------------------------------------------------------------- //-- Miscellaneous Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: miscLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -128,7 +131,7 @@ QGCView { } Rectangle { height: miscCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -359,7 +362,7 @@ QGCView { //----------------------------------------------------------------- //-- Save path - Row { + RowLayout { spacing: ScreenTools.defaultFontPixelWidth visible: _savePath.visible && !ScreenTools.isMobile @@ -368,8 +371,10 @@ QGCView { text: qsTr("File Save Path:") } QGCLabel { - anchors.baseline: savePathBrowse.baseline - text: _savePath.rawValue === "" ? qsTr("") : _savePath.value + anchors.baseline: savePathBrowse.baseline + Layout.maximumWidth: _panelWidth * 0.5 + elide: Text.ElideMiddle + text: _savePath.rawValue === "" ? qsTr("") : _savePath.value } QGCButton { id: savePathBrowse @@ -394,7 +399,7 @@ QGCView { //----------------------------------------------------------------- //-- RTK GPS Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: unitLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -407,7 +412,7 @@ QGCView { } Rectangle { height: rtkGrid.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -438,7 +443,7 @@ QGCView { //----------------------------------------------------------------- //-- Autoconnect settings Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: autoConnectLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -451,7 +456,7 @@ QGCView { } Rectangle { height: autoConnectCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -459,7 +464,7 @@ QGCView { Column { id: autoConnectCol - spacing: ScreenTools.defaultFontPixelWidth + spacing: ScreenTools.defaultFontPixelWidth * 2 anchors.centerIn: parent Row { @@ -480,7 +485,70 @@ QGCView { FactCheckBox { text: autoConnectRepeater.names[index] fact: modelData - visible: !ScreenTools.isiOS && modelData.visible + visible: modelData.visible + } + } + } + + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelWidth + visible: !ScreenTools.isMobile + && QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.visible + && QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.visible + + QGCLabel { + anchors.baseline: nmeaPortCombo.baseline + text: qsTr("NMEA GPS Device:") + width: _labelWidth + } + + QGCComboBox { + id: nmeaPortCombo + width: _editFieldWidth + model: ListModel { + ListElement { text: "disabled" } + } + + onActivated: { + if (index != -1) { + QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.value = textAt(index); + } + } + Component.onCompleted: { + for (var i in QGroundControl.linkManager.serialPorts) { + nmeaPortCombo.model.append({text:QGroundControl.linkManager.serialPorts[i]}) + } + var index = nmeaPortCombo.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.valueString); + nmeaPortCombo.currentIndex = index; + } + } + } + Row { + anchors.horizontalCenter: parent.horizontalCenter + spacing: ScreenTools.defaultFontPixelWidth + visible: !ScreenTools.isMobile + && QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.visible + && QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.visible + QGCLabel { + anchors.baseline: nmeaBaudCombo.baseline + text: qsTr("NMEA GPS Baudrate:") + width: _labelWidth + } + + QGCComboBox { + id: nmeaBaudCombo + width: _editFieldWidth + model: [4800, 9600, 19200, 38400, 57600, 115200] + + onActivated: { + if (index != -1) { + QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.value = textAt(index); + } + } + Component.onCompleted: { + var index = nmeaBaudCombo.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaBaud.valueString); + nmeaBaudCombo.currentIndex = index; } } } @@ -490,7 +558,7 @@ QGCView { //----------------------------------------------------------------- //-- Video Source Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: videoLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -503,7 +571,7 @@ QGCView { } Rectangle { height: videoCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -590,13 +658,13 @@ QGCView { spacing: ScreenTools.defaultFontPixelWidth visible: QGroundControl.videoManager.isGStreamer && videoSource.currentIndex && videoSource.currentIndex < 3 && QGroundControl.settingsManager.videoSettings.gridLines.visible QGCLabel { - text: qsTr("Grid Lines:") + text: qsTr("Disable When Disarmed:") width: _labelWidth anchors.verticalCenter: parent.verticalCenter } - FactComboBox { - width: _editFieldWidth - fact: QGroundControl.settingsManager.videoSettings.gridLines + FactCheckBox { + text: "" + fact: QGroundControl.settingsManager.videoSettings.disableWhenDisarmed anchors.verticalCenter: parent.verticalCenter } } @@ -605,7 +673,7 @@ QGCView { //----------------------------------------------------------------- //-- Video Source Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: videoRecLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -618,7 +686,7 @@ QGCView { } Rectangle { height: videoRecCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -676,7 +744,7 @@ QGCView { //----------------------------------------------------------------- //-- Custom Brand Image Item { - width: _qgcView.width * 0.8 + width: _panelWidth height: userBrandImageLabel.height anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter @@ -689,7 +757,7 @@ QGCView { } Rectangle { height: userBrandImageCol.height + (ScreenTools.defaultFontPixelHeight * 2) - width: _qgcView.width * 0.8 + width: _panelWidth color: qgcPal.windowShade anchors.margins: ScreenTools.defaultFontPixelWidth anchors.horizontalCenter: parent.horizontalCenter diff --git a/src/ui/preferences/MockLinkSettings.qml b/src/ui/preferences/MockLinkSettings.qml index e7d5cf1084b884b53bd432966deb73a840930de3..ede593528b2a42d4fd5597ac0a0e2a4d8796e903 100644 --- a/src/ui/preferences/MockLinkSettings.qml +++ b/src/ui/preferences/MockLinkSettings.qml @@ -35,6 +35,7 @@ Item { else subEditConfig.firmware = 0 subEditConfig.sendStatus = sendStatus.checked + subEditConfig.highLatency = highLatency.checked } Component.onCompleted: { @@ -49,6 +50,7 @@ Item { else copterVehicle.checked = true sendStatus.checked = subEditConfig.sendStatus + highLatency.checked = subEditConfig.highLatency } Column { @@ -67,6 +69,11 @@ Item { text: qsTr("Send Status Text and Voice") checked: false } + QGCCheckBox { + id: highLatency + text: qsTr("High latency") + checked: false + } Item { height: ScreenTools.defaultFontPixelHeight / 2 width: parent.width diff --git a/src/ui/preferences/SerialSettings.qml b/src/ui/preferences/SerialSettings.qml index 07e495605085212cc6b9e53f5d25f3cc975e32cf..8438549d070b9ad7a6a3b8306bc3168c26accf53 100644 --- a/src/ui/preferences/SerialSettings.qml +++ b/src/ui/preferences/SerialSettings.qml @@ -235,5 +235,19 @@ Item { } } } + QGCCheckBox { + text: "High Latency" + checked: false + visible: editConfig ? editConfig.highLatencyAllowed : false + onCheckedChanged: { + if(editConfig) { + editConfig.highLatency = checked + } + } + Component.onCompleted: { + if(editConfig) + checked = editConfig.highLatency + } + } } } diff --git a/src/ui/preferences/TcpSettings.qml b/src/ui/preferences/TcpSettings.qml index 182cf4d6167459bb9702999a11322ee114bc5044..6f0ac0e2d57ef50820f784181fded58f14d88a67 100644 --- a/src/ui/preferences/TcpSettings.qml +++ b/src/ui/preferences/TcpSettings.qml @@ -75,5 +75,19 @@ Item { anchors.verticalCenter: parent.verticalCenter } } + QGCCheckBox { + text: "High Latency" + checked: false + visible: editConfig ? editConfig.highLatencyAllowed : false + onCheckedChanged: { + if(editConfig) { + editConfig.highLatency = checked + } + } + Component.onCompleted: { + if(editConfig) + checked = editConfig.highLatency + } + } } } diff --git a/src/ui/preferences/UdpSettings.qml b/src/ui/preferences/UdpSettings.qml index 825b1bd79b80efe7fdd7ee78fa749790510817a8..6332dc964933134b017b95b9ac68944f2d238c3f 100644 --- a/src/ui/preferences/UdpSettings.qml +++ b/src/ui/preferences/UdpSettings.qml @@ -175,5 +175,19 @@ Item { } } } + QGCCheckBox { + text: "High Latency" + checked: false + visible: editConfig ? editConfig.highLatencyAllowed : false + onCheckedChanged: { + if(editConfig) { + editConfig.highLatency = checked + } + } + Component.onCompleted: { + if(editConfig) + checked = editConfig.highLatency + } + } } } diff --git a/src/ui/toolbar/MainToolBar.qml b/src/ui/toolbar/MainToolBar.qml index 2d2cb219d0819f1877eb71af912b3316b08daf8c..dffaa83e75d05011b7ccd8f091acede2d90cd442 100644 --- a/src/ui/toolbar/MainToolBar.qml +++ b/src/ui/toolbar/MainToolBar.qml @@ -34,6 +34,8 @@ Rectangle { signal showAnalyzeView signal armVehicle signal disarmVehicle + signal vtolTransitionToFwdFlight + signal vtolTransitionToMRFlight function checkSettingsButton() { settingsButton.checked = true diff --git a/src/ui/toolbar/VTOLModeIndicator.qml b/src/ui/toolbar/VTOLModeIndicator.qml new file mode 100644 index 0000000000000000000000000000000000000000..1e147d36c55dc3a9aab9da64eb45dfe021d1d684 --- /dev/null +++ b/src/ui/toolbar/VTOLModeIndicator.qml @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * (c) 2009-2016 QGROUNDCONTROL PROJECT + * + * QGroundControl is licensed according to the terms in the file + * COPYING.md in the root of the source code directory. + * + ****************************************************************************/ + +import QtQuick 2.3 +import QtQuick.Controls 1.2 +import QtQuick.Layouts 1.2 + +import QGroundControl 1.0 +import QGroundControl.Controls 1.0 +import QGroundControl.MultiVehicleManager 1.0 +import QGroundControl.ScreenTools 1.0 +import QGroundControl.Palette 1.0 + +//------------------------------------------------------------------------- +//-- VTOL Mode Indicator +QGCLabel { + anchors.top: parent.top + anchors.bottom: parent.bottom + verticalAlignment: Text.AlignVCenter + text: _fwdFlight ? qsTr("VTOL: Fixed Wing") : qsTr("VTOL: Multi-Rotor") + font.pointSize: ScreenTools.mediumFontPointSize + color: qgcPal.buttonText + visible: _activeVehicle ? _activeVehicle.vtol && _activeVehicle.px4Firmware : false + width: visible ? implicitWidth : 0 + + property var _activeVehicle: QGroundControl.multiVehicleManager.activeVehicle + property bool _fwdFlight: _activeVehicle ? _activeVehicle.vtolInFwdFlight : false + + QGCPalette { id: qgcPal } + + QGCMouseArea { + fillItem: parent + onClicked: _activeVehicle.vtolInFwdFlight ? toolBar.vtolTransitionToMRFlight() : toolBar.vtolTransitionToFwdFlight() + } +}