This commit is contained in:
kebab 2023-12-06 12:51:14 -05:00
commit 3f74bdc3c6
2418 changed files with 865694 additions and 0 deletions

246
CMakeLists.txt Normal file
View File

@ -0,0 +1,246 @@
cmake_minimum_required(VERSION 3.10)
project(QGroundControl LANGUAGES C CXX)
include(GNUInstallDirs)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel;Coverage")
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(FeatureSummary)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-Wall -Wextra)
endif()
# CMake build type
# Debug Release RelWithDebInfo MinSizeRel Coverage
if (NOT CMAKE_BUILD_TYPE)
# default to release with debug symbols
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
endif()
# Add folder where are supportive functions
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
# Configure Qt5 to get necessary variables
include(Qt5QGCConfiguration)
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Qt version: ${QT_VERSION}")
message(STATUS "Qt spec: ${QT_MKSPEC}")
set(COMPANY "Mavlink")
set(COPYRIGHT "Copyright (c) 2018 QGroundControl. All rights reserved.")
set(IDENTIFIER "io.mavlink.qgroundcontrol")
add_definitions(
-DQGC_APPLICATION_NAME="QGroundControl"
-DQGC_ORG_NAME="QGroundControl.org"
-DQGC_ORG_DOMAIN="org.qgroundcontrol"
)
include(Git)
message(STATUS "QGroundControl version: ${APP_VERSION_STR}")
#=============================================================================
# ccache
#
option(CCACHE "Use ccache if available" ON)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
#=============================================================================
# Compile QML
#
option(COMPILE_QML "Pre-compile QML files using the Qt Quick compiler." FALSE)
add_feature_info(COMPILE_QML COMPILE_QML "Pre-compile QML files using the Qt Quick compiler.")
if(COMPILE_QML)
find_package(Qt5QuickCompiler)
set_package_properties(Qt5QuickCompiler PROPERTIES
DESCRIPTION "Pre-compile QML files using the Qt Quick compiler."
TYPE OPTIONAL
)
endif()
#=============================================================================
# Debug QML
#
option(DEBUG_QML "Build QGroundControl with QML debugging/profiling support." FALSE)
add_feature_info(DEBUG_QML DEBUG_QML "Build QGroundControl with QML debugging/profiling support.")
if(DEBUG_QML)
message(STATUS "To enable the QML debugger/profiler, run with: '-qmljsdebugger=port:1234'")
add_definitions(-DQMLJSDEBUGGER)
add_definitions(-DQT_DECLARATIVE_DEBUG)
add_definitions(-DQT_QML_DEBUG)
endif()
#=============================================================================
# GStreamer
#
find_package(PkgConfig)
set(GST_DEPENDENCIES
gstreamer-1.0>=1.14
gstreamer-video-1.0>=1.14
gstreamer-gl-1.0>=1.14
)
if (MSVC)
pkg_check_modules(GST
${GST_DEPENDENCIES}
)
else()
pkg_check_modules(GST
${GST_DEPENDENCIES}
egl
)
endif()
if (GST_FOUND)
add_definitions(
-DQGC_GST_STREAMING
)
option(QGC_GST_MICROHARD_ENABLED "Enable microhard" OFF)
option(QGC_GST_TAISYNC_ENABLED "Enable taisyng" OFF)
else()
if (QGC_GST_MICROHARD_ENABLED OR QGC_GST_TAISYNC_ENABLED)
message(FATAL_ERROR "You tried to enable Microhard or Taisync but gstreamer is not found. Make sure to set PKG_CONFIG_EXECUTABLE and/or PKG_CONFIG_PATH properly.")
endif()
endif()
#=============================================================================
# Qt5
#
find_package(Qt5 ${QT_VERSION}
COMPONENTS
Bluetooth
Charts
Concurrent
Core
Location
Multimedia
Network
Positioning
Quick
QuickControls2
QuickWidgets
OpenGL
Sql
Svg
Test
TextToSpeech
Widgets
Xml
REQUIRED
HINTS
${QT_LIBRARY_HINTS}
)
if(NOT QT_MKSPEC MATCHES "winrt")
find_package(Qt5 ${QT_VERSION}
COMPONENTS
SerialPort
REQUIRED
HINTS
${QT_LIBRARY_HINTS}
)
endif()
# Sets the default flags for compilation and linking.
include(CompileOptions)
include_directories(
libs/eigen
libs/libevents
libs/mavlink/include/mavlink/v2.0
libs/mavlink/include/mavlink/v2.0/all
libs/mavlink/include/mavlink/v2.0/common
libs/shapelib
)
add_subdirectory(libs)
add_subdirectory(src)
set(QGC_RESOURCES
${CMAKE_CURRENT_SOURCE_DIR}/qgcimages.qrc
${CMAKE_CURRENT_SOURCE_DIR}/qgcresources.qrc
${CMAKE_CURRENT_SOURCE_DIR}/qgroundcontrol.qrc
${CMAKE_CURRENT_SOURCE_DIR}/resources/InstrumentValueIcons/InstrumentValueIcons.qrc
${CMAKE_CURRENT_SOURCE_DIR}/src/FirmwarePlugin/APM/APMResources.qrc
${CMAKE_CURRENT_SOURCE_DIR}/src/FirmwarePlugin/PX4/PX4Resources.qrc
${CMAKE_CURRENT_SOURCE_DIR}/VideoReceiverApp/qml.qrc
)
if (WIN32)
# append application icon resource for Windows
set(QGC_RESOURCES
${QGC_RESOURCES}
${CMAKE_CURRENT_SOURCE_DIR}/windows/QGroundControl.rc)
endif()
if(BUILD_TESTING)
list(APPEND QGC_RESOURCES
UnitTest.qrc
)
endif()
if(ANDROID)
add_library(QGroundControl SHARED ${QGC_RESOURCES})
else()
add_executable(QGroundControl ${QGC_RESOURCES})
endif()
target_link_libraries(QGroundControl PRIVATE qgc)
# Files/directories to install
install(
TARGETS QGroundControl
DESTINATION ${CMAKE_INSTALL_BINDIR}
)
install(
DIRECTORY ${CMAKE_SOURCE_DIR}/resources/
DESTINATION ${CMAKE_INSTALL_DATADIR}/qgroundcontrol
)
install(
FILES ${CMAKE_SOURCE_DIR}/deploy/org.mavlink.qgroundcontrol.desktop
DESTINATION ${CMAKE_INSTALL_DATADIR}/applications
)
install(
FILES ${CMAKE_SOURCE_DIR}/resources/icons/qgroundcontrol.png
DESTINATION ${CMAKE_INSTALL_DATADIR}/icons/hicolor/128x128/apps/
RENAME org.mavlink.qgroundcontrol.png
)
configure_file(
${CMAKE_SOURCE_DIR}/deploy/org.mavlink.qgroundcontrol.metainfo.xml.in
${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml
@ONLY
)
install(
FILES ${CMAKE_BINARY_DIR}/metainfo/org.mavlink.qgroundcontrol.metainfo.xml
DESTINATION ${CMAKE_INSTALL_DATADIR}/metainfo/
)
if(BUILD_TESTING)
target_link_libraries(QGroundControl PRIVATE Qt5::Test)
endif()
if(NOT QT_MKSPEC MATCHES "winrt")
target_link_libraries(QGroundControl
PUBLIC
Qt5::SerialPort
)
endif()
include(QGCDeploy)

46
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,46 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at lm@qgroundcontrol.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

9
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,9 @@
Thank you for considering to contribute to QGroundControl.
Contributions must be made under QGroundControl's dual-license system, under GPLv3 and Apache 2.0. This by definition rules out the re-use of any copyleft (e.g. GPL) licensed code. All contributions must be original or from a compatible license (BSD 2/3 clause, MIT, Apache 2.0).
* https://opensource.org/licenses/gpl-3.0.html
* https://opensource.org/licenses/Apache-2.0
Users of the codebase are free to use it under either license. The dual approach is necessary to be able to offer QGroundControl through the iOS and Android app stores and offers the open source community choice.

15
COPYING.md Normal file
View File

@ -0,0 +1,15 @@
# QGroundControl License
QGroundControl (QGC) is dual-licensed as Apache 2.0 and GPLv3. All contributions have to be made under both licenses (see [CONTRIBUTING](CONTRIBUTING.md)).
## Apache 2.0 License
The [Apache 2.0](http://www.apache.org/licenses/LICENSE-2.0) License is a permissive license which allows QGC to be built and used in any environment, including proprietary applications. It allows QGC to be built for mobile app stores. When building with Apache 2.0 a commercial Qt license is required.
## GPL v3 License
The [GPL v3 License](http://www.gnu.org/licenses/gpl-3.0.en.html) is a strong copyleft license. When building QGC under this license the open source version of Qt can be used. Our licensing grants the permission to use a later version of the license, however, contributions have to be made under 3.0.
## Contact
If you have questions regarding the licensing, please contact the maintainer Lorenz Meier, [lm@groundcontrol.org].

3
CentOS.md Normal file
View File

@ -0,0 +1,3 @@
# Running QGC on CentOS 7
This information has moved to the [QGroundControl Developer Guide](https://dev.qgroundcontrol.com/en/getting_started/CentOS.html).

197
ChangeLog.md Normal file
View File

@ -0,0 +1,197 @@
# QGroundControl Change Log
Note: This file only contains high level features or important fixes.
## 4.1 - Daily build
### 4.1.2 - Not yet released
* Bug: Radio setup - Fix double send of `MAV_CMD_PREFLIGHT_CALIBRATION` causing "Unable to send command" error.
### 4.1.1 - Stable
* Fix TCP link comms
### 4.1.0
* Support simple cameras which only support DIGICAM_CONTROL in the Photo/Video control on Fly View.
* Load Parameters From File: Support loading parameters which don't currently existing on the vehicle.
* Load Parameters From File: Add dialog which shows diff of file and vehicle params. Selective param upload from file.
* Video Streaming: New camera control supports capturing individual images from the stream
* Fly: Press and hold on arm button will change it to Force Arm. Click again to force arm.
* VTOL: General setting for transition distance which affects Plan takeoff, landing pattern creation
* VTOL: Much better VTOL support throughout QGC
* Maps: Support zoom up to level 23 even if map provider doesn't provide tiles that high
* Settings/Mavlink: Add ability to forward mavlink traffic out specified UDP port
* Support mavlink terrain protocol which queries gcs for terrain height information. Allows planning missions with TERRAIN\_FRAME.
* Fly: New instrument values display/editing support
* Plan: Added new VTOL Landing Pattern support
* Plan: Much better conversion of missions to KML for 3d visualization/verification of missions
* Plan: New Terrain Profile display including terrain collision indications on profile and in patterns (Survey, CorridorScan, etc)
* Fly: Rearchitect view and controls within for much better customization support in custom builds
## 4.0
## 4.0.9 - Not yet released
* Don't auto-connect to second Cube Orange/Yellow composite port
* Plan: Fix bugs associated with mission commands which specify and altitude but no lat/lon
* Fix bug which could prevent view switching from working after altitude mode warning dialog would pop up
## 4.0.8 - Stable
* iOS: Modify QGC file storage location to support new Files app
* Mobile: Fix Log Replay status bar file selection
## 4.0.7 - Stable
* Fix video page sizing
* Virtual Joystick: Fix right stick centering. Fix/add support for rover/sub reverse throttle support.
* Fix display of multiple ADSB vehicles
### 4.0.6 - Stable
* Analyze/Log Download - Fix download on mobile versions of QGC
* Fly: Fix problems where Continue Mission and Change Altitude were not available after a Mission Pause.
* PX4 Flow: Fix video display problem
### 4.0.5 - Stable
* Solo: Fix mission upload failures
* Plan: Fix crash when using Create Plan - Survey for fixed wing vehicle
### 4.0.4
* Mobile File Save: Fix problem with incorrect file extension being added
* Radio Setup: Fix problem with Spektrum bind
* Plan/Fly: Bring back waypoint number display in map items
### 4.0.3
* Plan: Add setting for takeoff item not required
* Plan: Takeoff item must be added prior to allowing other item types to enable
* Video: Add low latency mode as optional configuration setting (defaults to false)
* ArduPilot: Fix generated list of available firmwares
### 4.0.2
* Fix Mavlink V2 protocol negotation based on capability bits
* Fix waiting for AUTOPILOT_VERSION response to get capability bits
* ArduPilot: Above two fixes make fence/rally support enabling more reliable
### 4.0.1
* Fix ArduPilot current mission item tracking in Fly view
* Fix ADSB vehicle display
* Fix map positioning bug in Plan view
* Fix Windows 0xcc000007b startup error causes by incorrect VC runtimes being installed.
### 4.0.0
* Added ROI option during manual flight.
* Windows: Move builds to 64 bit, Qt 5.12.5
* Plan: ROI button will switch to Cancel ROI at appropriate times
* Plan: When ROI is selected the flight path lines which are affected by the ROI will change color
* ADSB: Added support for connecting to SBS server. Adds support for ADSB data from USB SDR Dongle running 'dump1090 --net' for example.
* Toolbar: Scrollable left/right on small screens like phones
* Plan View: New create plan UI for initial plan creation
* New Corridor editing tools ui. Includes ability to trace polyline by clicking.
* New Polygon editing tools ui. Includes ability to trace polygon by clicking.
* More performant flight path display algorithm. Mobile builds no longer show limited path length.
* ArduPilot: Add Motor Test vehicle setup page
* Compass Instrument: Add indicators for Home, COG and Next Waypoint headings.
* Log Replay: Support changing speed of playback
* Basic object avoidance added to vehicles.
* Added ability to set a joystick button to be single action or repeated action while the button is held down.
* Rework joysticks. Fixed several issues and updated setup UI.
* Adding support for UDP RTP h.265 video streams
* For text to speech engine on Linux to English (all messages are in English)
* Automated the ingestion of localization from Crowdin
* Automated the generation of language resources into the application
* Added all languages that come from Crowdin, even if empty.
* Allow dynamic language changes
* Check and respect camera storage status
* QGC now requires Qt 5.11 or greater. The idea is to standardize on Qt 5.12 (LTS). Just waiting for a solution for Windows as Qt dropped support for 32-bit.
* New, QtQuick MAVLink Inspector. The basics are already there but it still needs the ability to filter compID.
* Fixed application storage location on iOS. It was trying to save things where it could not.
* Basic support for secondary, thermal imaging with video streaming. If a camera provides both visual spectrum and thermal imaging, you have the option of displaying both at the same time.
* Better handling of fonts for Korean and Chinese locales. QGC now has builtin fonts for Korean (where some unusable font was being used). I still need to know if Chinese will need its own font as well.
* ArduPilot: Copter - Add suppor for Simple and Super Simple flight modes
* ArduPilot: Flight Mode setup - Switch Options were not showing up for all firmware revs
* ArduCopter: Add PID Tuning page to Tuning Setup
* ArduPilot: Copter - Advanced Tuning support
* ArduPilot: Rover - Frame setup support
* ArduPilot: Copter - Update support to 3.5+
* ArduPilot: Plane - Update support to 3.8+
* ArduPilot: Rover - Update support to 3.4+
* ArduPilot: Rework Airframe setup ui
* Plan/Pattern: Support named presets to simplify commonly used settings setup. Currently only supported by Survey.
* ArduCopter: Handle 3.7 parameter name change from CH#_OPT to RC#_OPTION.
* Improved support for flashing/connecting to ChibiOS bootloaders boards.
* Making the camera API available to all firmwares, not just PX4.
* ArduPilot: Support configurable mavlink stream rates. Available from Settings/Mavlink page.
* Major rewrite and bug fix pass through Structure Scan. Previous version had such bad problems that it can no longer be supported. Plans with Structure Scan will need to be recreated. New QGC will not load old Structure Scan plans.
## 3.5
### 3.5.5
* Fix mavlink message memset which cause wrong commands to be sent on ArduPilot GotoLocation.
* Disable Pause when fixed wing is on landing approach.
### 3.5.4
* Update windows drivers
* Add support for FMUK66 flashing/connection
* Guard against null geometry coming from gstreamer which can cause crashes
* Add .apj file selection support to custom firmware flash
### 3.5.3
* Change minimum RTK Survey-In limit to 0.01 meters
* Change Windows driver detection logic
* Fix crash when clicking on GeoFence polygon vertex
* PX4: Fix missing ```MC_YAW_FF``` parameter in PID Tuning
* ArduPilot: Fix parameter file save generating bad characters from git hash
### 3.5.2
* Fix Ubuntu AppImage startup failure
### 3.5.1
* Update Windows usb drivers
* Add ArduPilot CubeBlack Service Bulletin check
* Fix visibility of PX4/ArduPilot logo in toolbar
* Fix tile set count but in OfflineMaps which would cause image and elevation tile set to have incorrect counts and be incorrectly marked as download incomplete.
### 3.5.0
* Plan GeoFence: Fix loading of fence from intermediate 3.4 code
* Structure Scan: Fix loading of structure scan height
* ArduPilot: Fix location of planned home position when not connected to vehicle. Issue #6840.
* Fix loading of parameters from multiple components. Would report download complete too early, thus missing all default component params.
* Fix file delete in mobile file dialogs
* Add support for specifying fixed RTK based station location in Settings/General.
* Added Airmap integration to QGC
* Added ESTIMATOR_STATUS values to new estimatorStatus Vehicle FactGroup. These are now available to display in instrument panel.
* Added Chinese and Turkish localization and partial German localization.
* Make Distance to GCS available for display from instrument panel.
* Make Heading to Home available for display from instrument panel.
* Edit Position dialog available on polygon vertices.
* Fixed Wing Landing Pattern: Add stop photo/video support. Defaults to on such that doing an RTL will stop camera.
* Support loading polygons from SHP files
* Bumped settings version (now 8). This will cause all settings to be reset to defaults.
* Orbit visuals support changing rotation direction
* Added support for the Taisync 2.4GHz ViUlinx digital HD wireless link.
* Added UDP Port option for NMEA GPS Device.
## 3.4
### 3.4.4
* Stable desktop versions now inform user at boot if newer version is available.
* Multi-Vehicle Start Mission and Pause now work correctly. Issue #6864.
### 3.4.3
* Fix bug where Resume Mission would not display correctly in some cases. Issue #6835.
* Fix Planned Home Position altitude when no terrain data available. Issue #6846.
### 3.4.2
* Fix bug where new mission items may end up with 0 altitude internally and sent to vehicle while UI shows correct altitude. Issue #6823.
### 3.4.1
* Fix crash when Survery with terrain follow is moved quickly
* Fix terrain follow climb/descent rate fields swapped in ui

95
CodingStyle.cc Normal file
View File

@ -0,0 +1,95 @@
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
// This is an example class c++ file which is used to describe the QGroundControl
// coding style. In general almost everything in here has some coding style meaning.
// Not all style choices are explained.
#include "CodingStyle.h"
#include "QGCApplication.h"
#include <QFile>
#include <QDebug>
#include <math.h>
// Note how the Qt headers and the QGroundControl headers above are kept separate
Q_LOGGING_CATEGORY(CodingStyleLog, "CodingStyleLog")
const int CodingStyle::_privateStaticVariable = 0;
CodingStyle::CodingStyle(QObject* parent) :
QObject(parent),
_protectedVariable(1),
_privateVariable1(2),
_privateVariable2(3)
{
}
/// Document non-obvious private methods in the code file.
void CodingStyle::_privateMethod(void)
{
// Always include braces even for single line if/for/...
if (uas != _uasId) {
return;
}
// Note the brace placement
if (_lastSeenComponent == -1) {
_lastSeenComponent = component;
} else {
Q_ASSERT(component == _lastSeenComponent); // Asserts are your friend
}
}
void CodingStyle::_privateMethod2(void)
{
Fact* fact = qobject_cast<Fact*>(sender());
Q_ASSERT(fact);
QVariant typedValue;
switch (fact->type()) {
case FactMetaData::valueTypeInt8:
case FactMetaData::valueTypeInt16:
case FactMetaData::valueTypeInt32:
typedValue.setValue(QVariant(value.toInt()));
break;
case FactMetaData::valueTypeUint8:
case FactMetaData::valueTypeUint16:
case FactMetaData::valueTypeUint32:
typedValue.setValue(value.toUInt());
break;
case FactMetaData::valueTypeFloat: {
int localScopedVar = 1;
typedValue.setValue(value.toFloat());
}
break;
case FactMetaData::valueTypeDouble:
typedValue.setValue(value.toDouble());
break;
}
}
void CodingStyle::_methodWithManyArguments(QWidget* parent,
const QString& caption,
const QString& dir,
Options options1,
Options /* options2 */,
Options options3)
{
// options2 is an unused method argument.
// Do not use Q_UNUSUED and do not just remove the argument name and leave the type.
// Implementataion here...
}

80
CodingStyle.h Normal file
View File

@ -0,0 +1,80 @@
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
// This is an example class header file which is used to describe the QGroundControl
// coding style. In general almost everything in here has some coding style meaning.
// Not all style choices are explained.
#pragma once
#include <QObject>
#include <QMap>
#include <QXmlStreamReader>
#include <QLoggingCategory>
#include <limits.h>
#include "Fact.h"
#include "UASInterface.h"
// Note how the Qt headers, System, headers and the QGroundControl headers above are kept in separate groups
// If you are going to use a logging category for a class it should have the same name as the class
// with a suffix of Log.
Q_DECLARE_LOGGING_CATEGORY(CodingStyleLog)
/// Here is the class documentation. Class names are PascalCase. If you override any of the Qt base classes to provide
/// generic base implementations for widespread use prefix the class name with QGC. For example:
/// QGCMessageBox - is a QGC special vesion of Qt MessageBox
/// QGCPalette - is a QGC special version of Qt Palette
/// For normal single use classes do no prefix them name with QGC.
class CodingStyle : public QObject
{
Q_OBJECT
public:
CodingStyle(QObject* parent = NULL);
~CodingStyle();
/// Document public methods which are non-obvious in the header file
bool publicMethod1(void);
signals:
/// Document signals which are non-obvious in the header file
void qtSignal(void);
public slots:
// Public slots should only be used if the slot is connected to from another class. Majority of slots
// should be private.
void publicSlot(void);
// Don't use protected methods or variables unless the class is specifically meant to be used as a base class.
protected:
int _protectedVariable; ///< variable names are camelCase
void _protectedMethod(void); ///< method names are camelCase
private slots:
void _privateSlot(void);
private:
// Private methods and variable names begin with an "_". Documentation for
// non-obvious private methods goes in the code file, not the header.
void _privateMethod(void);
void _methodWithManyArguments(QWidget* parent, const QString& caption, const QString& dir, Options options1, Options options2, Options options3);
/// Document non-obvious variables in the header file. Long descriptions go here.
int _privateVariable1;
int _privateVariable2; ///< Short descriptions go here
static const int _privateStaticVariable;
};

64
CodingStyle.qml Normal file
View File

@ -0,0 +1,64 @@
/****************************************************************************
*
* (c) 2009-2016 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
import QtQuick 2.5
import QtQuick.Controls 1.3
import QGroundControl 1.0
import QGroundControl.ScreenTools 1.0
import QGroundControl.Controls 1.0
import QGroundControl.Palette 1.0
/// This is an example Qml file which is used to describe the QGroundControl coding style.
/// In general almost everything in here has some coding style meaning including order of
/// code. Not all style choices are explained. If there is any confusison please ask
/// and we'll answer and update style as needed.
Item {
// Property binding to item properties
width: ScreenTools.defaultFontPixelHeight * 10 // No hardcoded sizing. All sizing must be relative to a ScreenTools font size
height: ScreenTools.defaultFontPixelHeight * 20
// Property definitions available to consumers of this Qml Item come first
property int myIntProperty: 10
property real myRealProperty: 20.0
// Property definitions which are internal to the item are prepending with an underscore
// to signal private and come second
readonly property real _rectWidth: ScreenTools.defaultFontPixelWidth * 10 // Use readonly appropriately to increase binding performance
readonly property real _rectHeight: ScreenTools.defaultFontPixelWidth * 10
function myFunction() {
console.log("myFunction was called")
}
QGCPalette {
id: qgcPal // Note how id does not use an underscore
colorGroupEnabled: enabled
}
// You should always use the QGC provided variants of base control since they automatically support
// our theming and font support.
QGCButton {
// Also not how there is no id: specified for this control. Only add id: if it is needed.
text: "Button"
onClicked: myFunction()
}
Rectangle {
width: _rectWidth
height: _rectHeight
color: qgcPal.window // Use QGC palette colors for everything, no hardcoded colors
}
// For scoped blocks which are long include a comment so you can tell what the brace is matching.
// This is very handy when the top level brace scrolls off the screen. The endbrace comment in this
// specific file is only included as style info. This example code is not long enough to really need it.
} // Item - CodingStyle

22
Custom-Info.plist Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
<plist version="0.9">
<dict>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>CFBundleIconFile</key>
<string>macx.icns</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleVersion</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${QMAKE_FULL_VERSION}</string>
<key>CFBundleExecutable</key>
<string>${EXECUTABLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${PRODUCT_BUNDLE_IDENTIFIER}</string>
<key>NSCameraUsageDescription</key>
<string>Camera devices used for FPV</string>
</dict>
</plist>

14
ISSUE_TEMPLATE.md Normal file
View File

@ -0,0 +1,14 @@
# This system is for posting bugs or feature requests ONLY. Support questions will be automatically closed with no response.
# For questions about how to use or build QGC see: http://qgroundcontrol.com/#resources
----
# To post a bug report
When posting bug reports, include the following informaiton:
- Ground station operating system.
- QGroundControl version and build (daily, stable, self-built from source, etc.)
- **If you are using a Stable build which is not the latest, first step is to install latest Stable build and try again prior to entering a bug report.**
- Autopilot board: Pixhawk I, Pixhawk Mini, Pixhawk 2, etc.
- Autopilot firmware (e.g. PX4, ArduPilot, custom) and version.
- Exact steps to reproduce the problem. Starting from booting QGroundControl to when the problem occurs.
**All this template text should be deleted before creating your issue.**

135
Makefile Normal file
View File

@ -0,0 +1,135 @@
# Enforce the presence of the GIT repository
#
# We depend on our submodules, so we have to prevent attempts to
# compile without it being present.
ifeq ($(wildcard .git),)
$(error YOU HAVE TO USE GIT TO DOWNLOAD THIS REPOSITORY. ABORTING.)
endif
# explicity set default build target
all: linux
# Parsing
# --------------------------------------------------------------------
# assume 1st argument passed is the main target, the
# rest are arguments to pass to the makefile generated
# by cmake in the subdirectory
FIRST_ARG := $(firstword $(MAKECMDGOALS))
ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
j ?= 4
NINJA_BIN := ninja
ifndef NO_NINJA_BUILD
NINJA_BUILD := $(shell $(NINJA_BIN) --version 2>/dev/null)
ifndef NINJA_BUILD
NINJA_BIN := ninja-build
NINJA_BUILD := $(shell $(NINJA_BIN) --version 2>/dev/null)
endif
endif
ifdef NINJA_BUILD
PX4_CMAKE_GENERATOR := Ninja
PX4_MAKE := $(NINJA_BIN)
ifdef VERBOSE
PX4_MAKE_ARGS := -v
else
PX4_MAKE_ARGS :=
endif
else
ifdef SYSTEMROOT
# Windows
PX4_CMAKE_GENERATOR := "MSYS\ Makefiles"
else
PX4_CMAKE_GENERATOR := "Unix\ Makefiles"
endif
PX4_MAKE = $(MAKE)
PX4_MAKE_ARGS = -j$(j) --no-print-directory
endif
CMAKE_BUILD_TYPE ?= RelWithDebInfo
SRC_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST))))
# Functions
# --------------------------------------------------------------------
# describe how to build a cmake config
define cmake-build
+@$(eval BUILD_DIR = $(SRC_DIR)/build/$@$(BUILD_DIR_SUFFIX))
+@if [ $(PX4_CMAKE_GENERATOR) = "Ninja" ] && [ -e $(BUILD_DIR)/Makefile ]; then rm -rf $(BUILD_DIR); fi
+@if [ ! -e $(BUILD_DIR)/CMakeCache.txt ]; then mkdir -p $(BUILD_DIR) && cd $(BUILD_DIR) && cmake $(2) -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -G"$(PX4_CMAKE_GENERATOR)" -DQT_MKSPEC=$(1) || (rm -rf $(BUILD_DIR)); fi
+@(cd $(BUILD_DIR) && $(PX4_MAKE) $(PX4_MAKE_ARGS) $(ARGS))
endef
# Qt mkspec
# android_armv7 android_x86 gcc_64
gcc_64:
$(call cmake-build,$@,$(SRC_DIR))
android_armv7:
$(call cmake-build,$@,$(SRC_DIR))
android_x86:
$(call cmake-build,$@,$(SRC_DIR))
clang_64:
$(call cmake-build,$@,$(SRC_DIR))
xcode:
@mkdir -p build/xcode; cd build/xcode; cmake -GXcode -DCMAKE_BUILD_TYPE=RelWithDebInfo $(SRC_DIR)
linux: gcc_64
android: android_armv7
mac: clang_64
# Astyle
# --------------------------------------------------------------------
.PHONY: check_format format
check_format:
$(call colorecho,"Checking formatting with astyle")
@$(SRC_DIR)/Tools/astyle/check_code_style_all.sh
@cd $(SRC_DIR) && git diff --check
format:
$(call colorecho,"Formatting with astyle")
@$(SRC_DIR)/Tools/astyle/check_code_style_all.sh --fix
# Testing
# --------------------------------------------------------------------
.PHONY: tests tests_coverage
tests:
tests_coverage:
# Cleanup
# --------------------------------------------------------------------
.PHONY: clean submodulesclean submodulesupdate distclean
clean:
@rm -rf $(SRC_DIR)/build
submodulesclean:
@git submodule foreach --quiet --recursive git clean -ff -x -d
@git submodule update --quiet --init --recursive --force || true
@git submodule sync --recursive
@git submodule update --init --recursive --force
submodulesupdate:
@git submodule update --quiet --init --recursive || true
@git submodule sync --recursive
@git submodule update --init --recursive
distclean:
@git submodule deinit -f .
@git clean -ff -x -d -e ".project" -e ".cproject" -e ".idea" -e ".settings" -e ".vscode"

1
PULL_REQUEST_TEMPLATE.md Normal file
View File

@ -0,0 +1 @@

325
QGCCommon.pri Normal file
View File

@ -0,0 +1,325 @@
################################################################################
#
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
#
# QGroundControl is licensed according to the terms in the file
# COPYING.md in the root of the source code directory.
#
################################################################################
#
# This file contains configuration settings which are common to both the QGC Application and
# the Location Plugin. It should mainly contains initial CONFIG tag setup and compiler settings.
#
# Setup our supported build types. We do this once here and then use the defined config scopes
# to allow us to easily modify suported build types in one place instead of duplicated throughout
# the project file.
CONFIG -= debug_and_release
CONFIG += warn_on
CONFIG += resources_big
CONFIG += c++17
linux {
linux-g++ | linux-g++-64 | linux-g++-32 | linux-clang {
message("Linux build")
CONFIG += LinuxBuild
DEFINES += __STDC_LIMIT_MACROS
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += QGC_GST_MICROHARD_ENABLED
linux-clang {
message("Linux clang")
QMAKE_CXXFLAGS += -Qunused-arguments -fcolor-diagnostics
} else {
#QMAKE_CXXFLAGS += -H # Handy for debugging why something is getting built when an include file is touched
QMAKE_CXXFLAGS_WARN_ON += -Werror \
-Wno-deprecated-copy \ # These come from mavlink headers
-Wno-unused-parameter \ # gst_plugins-good has these errors
-Wno-implicit-fallthrough # gst_plugins-good has these errors
}
} else : linux-rasp-pi2-g++ {
message("Linux R-Pi2 build")
CONFIG += LinuxBuild
DEFINES += __STDC_LIMIT_MACROS __rasp_pi2__
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += QGC_GST_MICROHARD_ENABLED
} else : android-clang {
CONFIG += AndroidBuild MobileBuild
DEFINES += __android__
DEFINES += __STDC_LIMIT_MACROS
DEFINES += QGC_ENABLE_BLUETOOTH
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += QGC_GST_MICROHARD_ENABLED
QMAKE_CXXFLAGS_WARN_ON += -Werror \
-Wno-unused-parameter \ # gst_plugins-good has these errors
-Wno-implicit-fallthrough \ # gst_plugins-good has these errors
-Wno-unused-command-line-argument \ # from somewhere in Qt generated build files
-Wno-parentheses-equality # android gstreamer header files
QMAKE_CFLAGS_WARN_ON += \
-Wno-unused-command-line-argument # from somewhere in Qt generated build files
target.path = $$DESTDIR
equals(ANDROID_TARGET_ARCH, armeabi-v7a) {
DEFINES += __androidArm32__
message("Android Arm 32 bit build")
} else:equals(ANDROID_TARGET_ARCH, arm64-v8a) {
DEFINES += __androidArm64__
message("Android Arm 64 bit build")
} else:equals(ANDROID_TARGET_ARCH, x86) {
CONFIG += Androidx86Build
DEFINES += __androidx86__
message("Android x86 build")
} else {
error("Unsupported Android architecture: $${ANDROID_TARGET_ARCH}")
}
} else {
error("Unsuported Linux toolchain, only GCC 32- or 64-bit is supported")
}
} else : win32 {
contains(QMAKE_TARGET.arch, x86_64) {
message("Windows build")
CONFIG += WindowsBuild
DEFINES += __STDC_LIMIT_MACROS
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += QGC_GST_MICROHARD_ENABLED
QMAKE_CFLAGS -= -Zc:strictStrings
QMAKE_CFLAGS_RELEASE -= -Zc:strictStrings
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
QMAKE_CXXFLAGS -= -Zc:strictStrings
QMAKE_CXXFLAGS_RELEASE -= -Zc:strictStrings
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO -= -Zc:strictStrings
QMAKE_CXXFLAGS_WARN_ON += /WX /W3 \
/wd4005 \ # silence warnings about macro redefinition, these come from the shapefile code with is external
/wd4290 \ # ignore exception specifications
/wd4267 \ # silence conversion from 'size_t' to 'int', possible loss of data, these come from gps drivers shared with px4
/wd4100 # unreferenced formal parameter - gst-plugins-good
} else {
error("Unsupported Windows toolchain, only Visual Studio 2017 64 bit is supported")
}
} else : macx {
macx-clang | macx-llvm {
message("Mac build")
CONFIG += MacBuild
CONFIG += x86_64
CONFIG -= x86
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += QGC_GST_MICROHARD_ENABLED
QMAKE_CXXFLAGS += -fvisibility=hidden
QMAKE_CXXFLAGS_WARN_ON += -Werror \
-Wno-unused-parameter \ # gst-plugins-good
-Wno-unused-but-set-variable \ # eigen & QGCTileCacheWorker.cpp
-Wno-deprecated-declarations # eigen
} else {
error("Unsupported Mac toolchain, only 64-bit LLVM+clang is supported")
}
} else : ios {
message("iOS build")
CONFIG += iOSBuild MobileBuild app_bundle
CONFIG -= bitcode
DEFINES += __ios__
DEFINES += QGC_NO_GOOGLE_MAPS
DEFINES += NO_SERIAL_LINK
DEFINES += QGC_DISABLE_UVC
DEFINES += QGC_GST_TAISYNC_ENABLED
DEFINES += NO_SERIAL_LINK
QMAKE_IOS_DEPLOYMENT_TARGET = 11.0
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")
}
# Enable ccache where we can
linux|macx|ios {
system(which ccache) {
message("Found ccache, enabling")
!ios {
QMAKE_CXX = ccache $$QMAKE_CXX
QMAKE_CC = ccache $$QMAKE_CC
} else {
QMAKE_CXX = $$PWD/tools/iosccachecc.sh
QMAKE_CC = $$PWD/tools/iosccachecxx.sh
}
}
}
contains(DEFINES, NO_SERIAL_LINK) {
message("Serial port support disabled")
}
!MacBuild:!AndroidBuild {
# See QGCPostLinkCommon.pri for details on why MacBuild doesn't use DESTDIR
DESTDIR = staging
}
MobileBuild {
DEFINES += __mobile__
}
StableBuild {
message("Stable Build")
} else {
message("Daily Build")
DEFINES += DAILY_BUILD
}
# Set the QGC version from git
APP_VERSION_STR = vUnknown
VERSION = 0.0.0 # Marker to indicate out-of-tree build
MAC_VERSION = 0.0.0
MAC_BUILD = 0
exists ($$PWD/.git) {
GIT_DESCRIBE = $$system(git --git-dir $$PWD/.git --work-tree $$PWD describe --always --tags)
GIT_BRANCH = $$system(git --git-dir $$PWD/.git --work-tree $$PWD rev-parse --abbrev-ref HEAD)
GIT_HASH = $$system(git --git-dir $$PWD/.git --work-tree $$PWD rev-parse --short HEAD)
GIT_TIME = $$system(git --git-dir $$PWD/.git --work-tree $$PWD show --oneline --format=\"%ci\" -s HEAD)
message(GIT_DESCRIBE $${GIT_DESCRIBE})
# Pull the version info from the last annotated version tag. Format: v#.#.#
contains(GIT_DESCRIBE, ^v[0-9]+.[0-9]+.[0-9]+.*) {
APP_VERSION_STR = $${GIT_DESCRIBE}
VERSION = $$replace(GIT_DESCRIBE, "v", "")
VERSION = $$replace(VERSION, "-", ".")
VERSION = $$section(VERSION, ".", 0, 3)
}
DailyBuild {
APP_VERSION_STR = "Daily $${GIT_BRANCH}:$${GIT_HASH} $${GIT_TIME}"
}
message(QGroundControl APP_VERSION_STR VERSION $${APP_VERSION_STR} $${VERSION})
MacBuild {
MAC_VERSION = $$section(VERSION, ".", 0, 2)
MAC_BUILD = $$section(VERSION, ".", 3, 3)
message(QGroundControl MAC_VERSION MAC_BUILD $${MAC_VERSION} $${MAC_BUILD})
}
}
DEFINES += APP_VERSION_STR=\"\\\"$$APP_VERSION_STR\\\"\"
AndroidBuild {
QGC_ANDROID_PACKAGE = org.mavlink.qgroundcontrol
message(VERSION $${VERSION})
MAJOR_VERSION = $$section(VERSION, ".", 0, 0)
MINOR_VERSION = $$section(VERSION, ".", 1, 1)
PATCH_VERSION = $$section(VERSION, ".", 2, 2)
DEV_VERSION = $$section(VERSION, ".", 3, 3)
greaterThan(MAJOR_VERSION, 9) {
error(Major version larger than 1 digit: $${MAJOR_VERSION})
}
greaterThan(MINOR_VERSION, 9) {
error(Minor version larger than 1 digit: $${MINOR_VERSION})
}
greaterThan(PATCH_VERSION, 99) {
error(Patch version larger than 2 digits: $${PATCH_VERSION})
}
greaterThan(DEV_VERSION, 999) {
error(Dev version larger than 3 digits: $${DEV_VERSION})
}
lessThan(PATCH_VERSION, 10) {
PATCH_VERSION = $$join(PATCH_VERSION, "", "0")
}
equals(DEV_VERSION, "") {
DEV_VERSION = "0"
}
lessThan(DEV_VERSION, 10) {
DEV_VERSION = $$join(DEV_VERSION, "", "0")
}
lessThan(DEV_VERSION, 100) {
DEV_VERSION = $$join(DEV_VERSION, "", "0")
}
# Bitness for android version number is 66/34 instead of 64/32 in because of a required version number bump screw-up ages ago
equals(ANDROID_TARGET_ARCH, arm64-v8a) {
ANDROID_TRUE_BITNESS = 64
ANDROID_VERSION_BITNESS = 66
} else {
ANDROID_TRUE_BITNESS = 32
ANDROID_VERSION_BITNESS = 34
}
# Version code format: BBMIPPDDD (B=Bitness, I=Minor)
ANDROID_VERSION_CODE = "BBMIPPDDD"
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "BB", $$ANDROID_VERSION_BITNESS)
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "M", $$MAJOR_VERSION)
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "I", $$MINOR_VERSION)
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "PP", $$PATCH_VERSION)
ANDROID_VERSION_CODE = $$replace(ANDROID_VERSION_CODE, "DDD", $$DEV_VERSION)
message(Android version info: $${ANDROID_VERSION_CODE} bitness:$${ANDROID_VERSION_BITNESS} major:$${MAJOR_VERSION} minor:$${MINOR_VERSION} patch:$${PATCH_VERSION} dev:$${DEV_VERSION})
ANDROID_VERSION_NAME = APP_VERSION_STR
}
DEFINES += EIGEN_MPL2_ONLY
# Installer configuration
installer {
CONFIG -= debug
CONFIG -= debug_and_release
CONFIG += release
message(Build Installer)
}
# Setup our supported build flavors
CONFIG(debug, debug|release) {
message(Debug flavor)
CONFIG += DebugBuild
} else:CONFIG(release, debug|release) {
message(Release flavor)
CONFIG += ReleaseBuild
} else {
error(Unsupported build flavor)
}
# Setup our build directories
SOURCE_DIR = $$IN_PWD
LANGUAGE = C++
LOCATION_PLUGIN_DESTDIR = $${OUT_PWD}/src/QtLocationPlugin
LOCATION_PLUGIN_NAME = QGeoServiceProviderFactoryQGC
# Turn off serial port warnings
DEFINES += _TTY_NOWARN_
MacBuild {
QMAKE_TARGET_BUNDLE_PREFIX = org.qgroundcontrol
QMAKE_BUNDLE = qgroundcontrol
}
#
# Build-specific settings
#
ReleaseBuild {
DEFINES += QT_NO_DEBUG QT_MESSAGELOGCONTEXT
CONFIG += force_debug_info # Enable debugging symbols on release builds
!iOSBuild {
!AndroidBuild {
CONFIG += ltcg # Turn on link time code generation
}
}
WindowsBuild {
# Run compilation using VS compiler using multiple threads
QMAKE_CXXFLAGS += -MP
# Enable function level linking and enhanced optimized debugging
QMAKE_CFLAGS_RELEASE += /Gy /Zo
QMAKE_CXXFLAGS_RELEASE += /Gy /Zo
QMAKE_CFLAGS_RELEASE_WITH_DEBUGINFO += /Gy /Zo
QMAKE_CXXFLAGS_RELEASE_WITH_DEBUGINFO += /Gy /Zo
# Eliminate duplicate COMDATs
QMAKE_LFLAGS_RELEASE += /OPT:ICF
QMAKE_LFLAGS_RELEASE_WITH_DEBUGINFO += /OPT:ICF
}
}

266
QGCExternalLibs.pri Normal file
View File

@ -0,0 +1,266 @@
################################################################################
#
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
#
# QGroundControl is licensed according to the terms in the file
# COPYING.md in the root of the source code directory.
#
################################################################################
#
# [REQUIRED] Add support for <inttypes.h> to Windows.
#
WindowsBuild {
INCLUDEPATH += libs/msinttypes
}
#
# [REQUIRED] Add support for the MAVLink communications protocol.
#
# 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 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.
# In the mean time, its 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 = $$SOURCE_DIR/$$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 = all
message($$sprintf("Using MAVLink dialect '%1'.", $$MAVLINK_CONF))
}
}
# If defined, all APM specific MAVLink messages are disabled
contains (CONFIG, QGC_DISABLE_APM_MAVLINK) {
message("Disable APM MAVLink support")
DEFINES += NO_ARDUPILOT_DIALECT
CONFIG += ArdupilotDisabled
} else {
CONFIG += ArdupilotEnabled
}
# Then we add the proper include paths dependent on the dialect.
INCLUDEPATH += $$MAVLINKPATH
count(MAVLINK_CONF, 1) {
exists($$MAVLINKPATH/$$MAVLINK_CONF) {
INCLUDEPATH += $$MAVLINKPATH/$$MAVLINK_CONF
DEFINES += $$sprintf('QGC_USE_%1_MESSAGES', $$upper($$MAVLINK_CONF))
} else {
error($$sprintf("MAVLink dialect '%1' does not exist at '%2'!", $$MAVLINK_CONF, $$MAVLINKPATH_REL))
}
} else {
error(Only a single mavlink dialect can be specified in MAVLINK_CONF)
}
#
# [REQUIRED] EIGEN matrix library
# NOMINMAX constant required to make internal min/max work.
INCLUDEPATH += libs/eigen
DEFINES += NOMINMAX
#
# [REQUIRED] Events submodule
HEADERS+= \
libs/libevents/libevents/libs/cpp/protocol/receive.h \
libs/libevents/libevents/libs/cpp/parse/health_and_arming_checks.h \
libs/libevents/libevents/libs/cpp/parse/parser.h \
libs/libevents/libevents/libs/cpp/generated/events_generated.h \
libs/libevents/libevents_definitions.h
SOURCES += \
libs/libevents/libevents/libs/cpp/protocol/receive.cpp \
libs/libevents/libevents/libs/cpp/parse/health_and_arming_checks.cpp \
libs/libevents/libevents/libs/cpp/parse/parser.cpp \
libs/libevents/definitions.cpp
INCLUDEPATH += \
libs/libevents \
libs/libevents/libs/cpp/parse
#
# [REQUIRED] shapelib library
INCLUDEPATH += libs/shapelib
SOURCES += \
libs/shapelib/shpopen.c \
libs/shapelib/safileio.c
#
# [REQUIRED] zlib library
WindowsBuild {
INCLUDEPATH += $$SOURCE_DIR/libs/zlib/windows/include
LIBS += -L$$SOURCE_DIR/libs/zlib/windows/lib
LIBS += -lzlibstat
} else {
LIBS += -lz
}
#
# [REQUIRED] LZMA decompression library
HEADERS+= \
libs/xz-embedded/linux/include/linux/xz.h \
libs/xz-embedded/linux/lib/xz/xz_lzma2.h \
libs/xz-embedded/linux/lib/xz/xz_private.h \
libs/xz-embedded/linux/lib/xz/xz_stream.h \
libs/xz-embedded/userspace/xz_config.h
SOURCES += \
libs/xz-embedded/linux/lib/xz/xz_crc32.c \
libs/xz-embedded/linux/lib/xz/xz_crc64.c \
libs/xz-embedded/linux/lib/xz/xz_dec_lzma2.c \
libs/xz-embedded/linux/lib/xz/xz_dec_stream.c
INCLUDEPATH += \
libs/xz-embedded/userspace \
libs/xz-embedded/linux/include/linux
DEFINES += XZ_DEC_ANY_CHECK XZ_USE_CRC64
# [REQUIRED] QMDNS Engine
HEADERS+= \
libs/qmdnsengine_export.h \
libs/qmdnsengine/src/src/bitmap_p.h \
libs/qmdnsengine/src/src/browser_p.h \
libs/qmdnsengine/src/src/cache_p.h \
libs/qmdnsengine/src/src/hostname_p.h \
libs/qmdnsengine/src/src/message_p.h \
libs/qmdnsengine/src/src/prober_p.h \
libs/qmdnsengine/src/src/provider_p.h \
libs/qmdnsengine/src/src/query_p.h \
libs/qmdnsengine/src/src/record_p.h \
libs/qmdnsengine/src/src/resolver_p.h \
libs/qmdnsengine/src/src/server_p.h \
libs/qmdnsengine/src/src/service_p.h \
libs/qmdnsengine/src/include/qmdnsengine/abstractserver.h \
libs/qmdnsengine/src/include/qmdnsengine/bitmap.h \
libs/qmdnsengine/src/include/qmdnsengine/browser.h \
libs/qmdnsengine/src/include/qmdnsengine/cache.h \
libs/qmdnsengine/src/include/qmdnsengine/dns.h \
libs/qmdnsengine/src/include/qmdnsengine/hostname.h \
libs/qmdnsengine/src/include/qmdnsengine/mdns.h \
libs/qmdnsengine/src/include/qmdnsengine/message.h \
libs/qmdnsengine/src/include/qmdnsengine/prober.h \
libs/qmdnsengine/src/include/qmdnsengine/provider.h \
libs/qmdnsengine/src/include/qmdnsengine/query.h \
libs/qmdnsengine/src/include/qmdnsengine/record.h \
libs/qmdnsengine/src/include/qmdnsengine/resolver.h \
libs/qmdnsengine/src/include/qmdnsengine/server.h \
libs/qmdnsengine/src/include/qmdnsengine/service.h
SOURCES += \
libs/qmdnsengine/src/src/abstractserver.cpp \
libs/qmdnsengine/src/src/bitmap.cpp \
libs/qmdnsengine/src/src/browser.cpp \
libs/qmdnsengine/src/src/cache.cpp \
libs/qmdnsengine/src/src/dns.cpp \
libs/qmdnsengine/src/src/hostname.cpp \
libs/qmdnsengine/src/src/mdns.cpp \
libs/qmdnsengine/src/src/message.cpp \
libs/qmdnsengine/src/src/prober.cpp \
libs/qmdnsengine/src/src/provider.cpp \
libs/qmdnsengine/src/src/query.cpp \
libs/qmdnsengine/src/src/record.cpp \
libs/qmdnsengine/src/src/resolver.cpp \
libs/qmdnsengine/src/src/server.cpp \
libs/qmdnsengine/src/src/service.cpp
INCLUDEPATH += \
libs/ \
libs/qmdnsengine/src/include/ \
libs/qmdnsengine/src/src/
#
# [REQUIRED] SDL dependency. Provides joystick/gamepad support.
# The SDL is packaged with QGC for the Mac and Windows. Linux support requires installing the SDL
# library (development libraries and static binaries).
#
MacBuild {
INCLUDEPATH += \
$$SOURCE_DIR/libs/Frameworks/SDL2.framework/Headers
LIBS += \
-F$$SOURCE_DIR/libs/Frameworks \
-framework SDL2
} else:LinuxBuild {
PKGCONFIG = sdl2
} else:WindowsBuild {
INCLUDEPATH += $$SOURCE_DIR/libs/sdl2/msvc/include
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Windows/x64/include
LIBS += -L$$SOURCE_DIR/libs/sdl2/msvc/lib/x64
LIBS += -lSDL2
}
# Include Android OpenSSL libs
AndroidBuild {
include($$SOURCE_DIR/libs/OpenSSL/android_openssl/openssl.pri)
#message("ANDROID_EXTRA_LIBS")
#message($$ANDROID_TARGET_ARCH)
#message($$ANDROID_EXTRA_LIBS)
}
# Pairing
contains(DEFINES, QGC_ENABLE_PAIRING) {
MacBuild {
#- Pairing is generally not supported on macOS. This is here solely for development.
exists(/usr/local/Cellar/openssl/1.0.2t/include) {
INCLUDEPATH += /usr/local/Cellar/openssl/1.0.2t/include
LIBS += -L/usr/local/Cellar/openssl/1.0.2t/lib
LIBS += -lcrypto
} else {
# There is some circular reference settings going on between QGCExternalLibs.pri and gqgroundcontrol.pro.
# So this duplicates some of the enable/disable logic which would normally be in qgroundcontrol.pro.
DEFINES -= QGC_ENABLE_PAIRING
}
} else:WindowsBuild {
#- Pairing is not supported on Windows
DEFINES -= QGC_ENABLE_PAIRING
} else {
LIBS += -lcrypto
AndroidBuild {
contains(QT_ARCH, arm) {
LIBS += $$ANDROID_EXTRA_LIBS
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Android/arch-armeabi-v7a/include
} else {
LIBS += $$ANDROID_EXTRA_LIBS
INCLUDEPATH += $$SOURCE_DIR/libs/OpenSSL/Android/arch-x86/include
}
}
}
}
#
# [OPTIONAL] Zeroconf for UDP links
#
contains (DEFINES, DISABLE_ZEROCONF) {
message("Skipping support for Zeroconf (manual override from command line)")
DEFINES -= DISABLE_ZEROCONF
# Otherwise the user can still disable this feature in the user_config.pri file.
} else:exists(user_config.pri):infile(user_config.pri, DEFINES, DISABLE_ZEROCONF) {
message("Skipping support for Zeroconf (manual override from user_config.pri)")
# Mac support is built into OS
} else:MacBuild|iOSBuild {
message("Including support for Zeroconf (Bonjour)")
DEFINES += QGC_ZEROCONF_ENABLED
} else {
message("Skipping support for Zeroconf (unsupported platform)")
}

166
QGCPostLinkCommon.pri Normal file
View File

@ -0,0 +1,166 @@
################################################################################
#
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
#
# QGroundControl is licensed according to the terms in the file
# COPYING.md in the root of the source code directory.
#
################################################################################
# These are the Post Link steps which are common to all builds
QMAKE_POST_LINK += echo "Post Link Common"
#
# Perform platform specific setup
#
MacBuild {
# Qt is screwed up if you use qmake to create an XCode Project which has a DESTDIR set on it.
# This is because XCode builds create the .app in BUILT_PRODUCTS_DIR. If you use a DESTDIR then
# Qt adds a Copy Phase to the build which copies the .app from the BUILT_PRODUCTS_DIR to DESTDIR.
# This causes all sort of problem which are too long to list here. In order to work around this
# We have to duplicate the post link commands here to work from two different locations. And to deal
# with the differences between post list command running in a shell script (XCode) versus a makefile (Qt Creator)
macx-xcode {
# SDL2 Framework
QMAKE_POST_LINK += && rsync -a --delete $$SOURCE_DIR/libs/Frameworks/SDL2.framework $BUILT_PRODUCTS_DIR/$${TARGET}.app/Contents/Frameworks
QMAKE_POST_LINK += && install_name_tool -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $BUILT_PRODUCTS_DIR/$${TARGET}.app/Contents/MacOS/$${TARGET}
} else {
# SDL2 Framework
QMAKE_POST_LINK += && rsync -a --delete $$SOURCE_DIR/libs/Frameworks/SDL2.framework $${TARGET}.app/Contents/Frameworks
QMAKE_POST_LINK += && install_name_tool -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $${TARGET}.app/Contents/MacOS/$${TARGET}
}
}
WindowsBuild {
#BASEDIR_WIN = $$replace(SOURCE_DIR, "/", "\\")
QT_BIN_DIR = $$dirname(QMAKE_QMAKE)
# Copy dependencies
DebugBuild: DLL_QT_DEBUGCHAR = "d"
ReleaseBuild: DLL_QT_DEBUGCHAR = ""
COPY_FILE_LIST = \
$$SOURCE_DIR\\libs\\sdl2\\msvc\\lib\\x64\\SDL2.dll \
$$SOURCE_DIR\\libs\\OpenSSL\\windows\\libcrypto-1_1-x64.dll \
$$SOURCE_DIR\\libs\\OpenSSL\\windows\\libssl-1_1-x64.dll
for(COPY_FILE, COPY_FILE_LIST) {
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$COPY_FILE\" \"$$DESTDIR\"
}
ReleaseBuild {
# Copy Visual Studio DLLs
# Note that this is only done for release because the debugging versions of these DLLs cannot be redistributed.
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\msvcp140.dll\" \"$$DESTDIR\"
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\msvcp140_1.dll\" \"$$DESTDIR\"
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\vcruntime140.dll\" \"$$DESTDIR\"
QMAKE_POST_LINK += $$escape_expand(\\n) $$QMAKE_COPY \"$$SOURCE_DIR\\libs\\Microsoft\\windows\\vcruntime140_1.dll\" \"$$DESTDIR\"
}
DEPLOY_TARGET = $$shell_quote($$shell_path($$DESTDIR\\$${TARGET}.exe))
QMAKE_POST_LINK += $$escape_expand(\\n) $$QT_BIN_DIR\\windeployqt --qmldir=$${SOURCE_DIR}\\src $${DEPLOY_TARGET}
}
LinuxBuild {
QMAKE_POST_LINK += && mkdir -p $$DESTDIR/Qt/libs && mkdir -p $$DESTDIR/Qt/plugins
QMAKE_RPATHDIR += $ORIGIN/Qt/libs
# QT_INSTALL_LIBS
QT_LIB_LIST += \
libQt5Charts.so.5 \
libQt5Core.so.5 \
libQt5DBus.so.5 \
libQt5Gui.so.5 \
libQt5Location.so.5 \
libQt5Multimedia.so.5 \
libQt5MultimediaQuick.so.5 \
libQt5Network.so.5 \
libQt5OpenGL.so.5 \
libQt5Positioning.so.5 \
libQt5PositioningQuick.so.5 \
libQt5PrintSupport.so.5 \
libQt5Qml.so.5 \
libQt5QmlModels.so.5 \
libQt5QmlWorkerScript.so.5 \
libQt5Quick.so.5 \
libQt5QuickControls2.so.5 \
libQt5QuickShapes.so.5 \
libQt5QuickTemplates2.so.5 \
libQt5QuickWidgets.so.5 \
libQt5SerialPort.so.5 \
libQt5Sql.so.5 \
libQt5Svg.so.5 \
libQt5Test.so.5 \
libQt5Widgets.so.5 \
libQt5X11Extras.so.5 \
libQt5XcbQpa.so.5 \
libQt5Xml.so.5 \
libicui18n.so* \
libQt5TextToSpeech.so.5
# Not all Qt libs are built in all systems. CI doesn't build Wayland, for example.
QT_LIB_OPTIONALS = \
libQt5WaylandClient.so.5 \
libQt5WaylandCompositor.so.5
for(QT_LIB, QT_LIB_OPTIONALS) {
exists("$$[QT_INSTALL_LIBS]/$$QT_LIB") {
QT_LIB_LIST += $$QT_LIB
}
}
exists($$[QT_INSTALL_LIBS]/libicudata.so.56) {
# Some Qt distributions link with *.so.56
QT_LIB_LIST += \
libicudata.so.56 \
libicui18n.so.56 \
libicuuc.so.56
} else {
QT_LIB_LIST += \
libicudata.so \
libicui18n.so \
libicuuc.so
}
# Copy only if non-existing to avoid file timestamp updates
for(QT_LIB, QT_LIB_LIST) {
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference $$[QT_INSTALL_LIBS]/$$QT_LIB $$DESTDIR/Qt/libs/
}
# QT_INSTALL_PLUGINS
QT_PLUGIN_LIST = \
bearer \
geoservices \
iconengines \
imageformats \
platforminputcontexts \
platforms \
position \
sqldrivers \
texttospeech
!contains(DEFINES, __rasp_pi2__) {
QT_PLUGIN_LIST += xcbglintegrations
}
for(QT_PLUGIN, QT_PLUGIN_LIST) {
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference --recursive $$[QT_INSTALL_PLUGINS]/$$QT_PLUGIN $$DESTDIR/Qt/plugins/
}
# QT_INSTALL_QML
QMAKE_POST_LINK += && $$QMAKE_COPY -n --dereference --recursive $$[QT_INSTALL_QML] $$DESTDIR/Qt/
# QGroundControl start script
contains (CONFIG, QGC_DISABLE_CUSTOM_BUILD) | !exists($$PWD/custom/custom.pri) {
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/deploy/qgroundcontrol-start.sh $$DESTDIR
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/deploy/qgroundcontrol.desktop $$DESTDIR
QMAKE_POST_LINK += && $$QMAKE_COPY $$SOURCE_DIR/resources/icons/qgroundcontrol.png $$DESTDIR
} else {
include($$PWD/custom/custom_deploy.pri)
}
QMAKE_POST_LINK += && SEARCHDIR="$$DESTDIR/Qt" RPATHDIR="$$DESTDIR/Qt/libs" "$$PWD/deploy/linux-fixup-rpaths.bash"
# https://doc.qt.io/qt-5/qt-conf.html
QMAKE_POST_LINK += && $$QMAKE_COPY "$$SOURCE_DIR/deploy/qt.conf" "$$DESTDIR"
}

66
QGCPostLinkInstaller.pri Normal file
View File

@ -0,0 +1,66 @@
################################################################################
#
# (c) 2009-2020 QGROUNDCONTROL PROJECT <http://www.qgroundcontrol.org>
#
# QGroundControl is licensed according to the terms in the file
# COPYING.md in the root of the source code directory.
#
################################################################################
# These are the Post Link steps which are specific to installer builds
installer {
DEFINES += QGC_INSTALL_RELEASE
MacBuild {
QMAKE_POST_LINK += && echo macdeployqt
QMAKE_POST_LINK += && $$dirname(QMAKE_QMAKE)/macdeployqt $${TARGET}.app -appstore-compliant -verbose=1 -qmldir=$${SOURCE_DIR}/src
# macdeployqt is missing some relocations once in a while. "Fix" it:
QMAKE_POST_LINK += && rsync -a --delete /Library/Frameworks/GStreamer.framework $${TARGET}.app/Contents/Frameworks
QMAKE_POST_LINK += && echo libexec
QMAKE_POST_LINK += && ln -sf $${TARGET}.app/Contents/Frameworks $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/libexec/Frameworks
QMAKE_POST_LINK += && install_name_tool -change /Library/Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer @executable_path/../Frameworks/GStreamer.framework/Versions/1.0/lib/GStreamer $${TARGET}.app/Contents/MacOS/$${TARGET}
QMAKE_POST_LINK += && rm -rf $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/{bin,etc,share,Headers,include,Commands}
QMAKE_POST_LINK += && rm -rf $${TARGET}.app/Contents/Frameworks/GStreamer.framework/Versions/1.0/lib/{*.a,*.la,glib-2.0,gst-validate-launcher,pkgconfig}
codesign {
# Disabled for now since it's not working correctly yet
#QMAKE_POST_LINK += && echo codesign
#QMAKE_POST_LINK += && codesign --deep $${TARGET}.app -s WQREC9W69J
}
# Create package
QMAKE_POST_LINK += && echo hdiutil
QMAKE_POST_LINK += && mkdir -p package
QMAKE_POST_LINK += && mkdir -p staging
QMAKE_POST_LINK += && rsync -a --delete $${TARGET}.app staging
QMAKE_POST_LINK += && rm -rf /tmp/tmp.dmg
QMAKE_POST_LINK += && rm -rf package/$${TARGET}.dmg
QMAKE_POST_LINK += && hdiutil create /tmp/tmp.dmg -ov -volname "$${TARGET}-$${MAC_VERSION}" -fs HFS+ -srcfolder "staging"
QMAKE_POST_LINK += && hdiutil convert /tmp/tmp.dmg -format UDBZ -o package/$${TARGET}.dmg
QMAKE_POST_LINK += && rm /tmp/tmp.dmg
}
WindowsBuild {
QMAKE_POST_LINK += $$escape_expand(\\n) $$quote("\"C:\\Program Files \(x86\)\\NSIS\\makensis.exe\"" $$(QGC_NSIS_INSTALLER_PARAMETERS) /DDRIVER_MSI="\"$${QGC_INSTALLER_DRIVER_MSI}\"" /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}\\$${TARGET}-installer.exe\"" "\"$${QGC_INSTALLER_SCRIPT}\"")
OTHER_FILES += $${QGC_INSTALLER_SCRIPT}
}
LinuxBuild {
#-- TODO: This uses hardcoded paths. It should use $${DESTDIR}
QMAKE_POST_LINK += && mkdir -p package
QMAKE_POST_LINK += && tar -cj --exclude='package' -f package/QGroundControl.tar.bz2 staging --transform 's/$${DESTDIR}/qgroundcontrol/'
}
AndroidBuild {
_ANDROID_KEYSTORE_PASSWORD = $$(ANDROID_KEYSTORE_PASSWORD)
QMAKE_POST_LINK += && mkdir -p package
isEmpty(_ANDROID_KEYSTORE_PASSWORD) {
message(Keystore password not available - not signing package)
# This is for builds in forks and PR where the Android keystore password is not available
QMAKE_POST_LINK += && make apk
QMAKE_POST_LINK += && cp android-build/build/outputs/apk/debug/android-build-debug.apk package/QGroundControl$${ANDROID_TRUE_BITNESS}.apk
} else {
QMAKE_POST_LINK += && make apk_install_target INSTALL_ROOT=android-build
QMAKE_POST_LINK += && androiddeployqt --verbose --input android-QGroundControl-deployment-settings.json --output android-build --release --sign $${SOURCE_DIR}/android/android_release.keystore QGCAndroidKeyStore --storepass $$(ANDROID_KEYSTORE_PASSWORD)
QMAKE_POST_LINK += && cp android-build/build/outputs/apk/release/android-build-release-signed.apk package/QGroundControl$${ANDROID_TRUE_BITNESS}.apk
}
}
}

20
README.md Normal file
View File

@ -0,0 +1,20 @@
# QGroundControl Ground Control Station
[![Releases](https://img.shields.io/github/release/mavlink/QGroundControl.svg)](https://github.com/mavlink/QGroundControl/releases)
*QGroundControl* (QGC) is an intuitive and powerful ground control station (GCS) for UAVs.
The primary goal of QGC is ease of use for both first time and professional users.
It provides full flight control and mission planning for any MAVLink enabled drone, and vehicle setup for both PX4 and ArduPilot powered UAVs. Instructions for *using QGroundControl* are provided in the [User Manual](https://docs.qgroundcontrol.com/en/) (you may not need them because the UI is very intuitive!)
All the code is open-source, so you can contribute and evolve it as you want.
The [Developer Guide](https://dev.qgroundcontrol.com/en/) explains how to [build](https://dev.qgroundcontrol.com/en/getting_started/) and extend QGC.
Key Links:
* [Website](http://qgroundcontrol.com) (qgroundcontrol.com)
* [User Manual](https://docs.qgroundcontrol.com/en/)
* [Developer Guide](https://dev.qgroundcontrol.com/en/)
* [Discussion/Support](https://docs.qgroundcontrol.com/en/Support/Support.html)
* [Contributing](https://dev.qgroundcontrol.com/en/contribute/)
* [License](https://github.com/mavlink/qgroundcontrol/blob/master/COPYING.md)

24
UnitTest.qrc Normal file
View File

@ -0,0 +1,24 @@
<RCC>
<qresource prefix="/unittest">
<file alias="SectionTest.plan">src/MissionManager/UnitTest/SectionTest.plan</file>
<file alias="UT-MavCmdInfoCommon.json">src/MissionManager/UnitTest/UT-MavCmdInfoCommon.json</file>
<file alias="UT-MavCmdInfoFixedWing.json">src/MissionManager/UnitTest/UT-MavCmdInfoFixedWing.json</file>
<file alias="UT-MavCmdInfoMultiRotor.json">src/MissionManager/UnitTest/UT-MavCmdInfoMultiRotor.json</file>
<file alias="UT-MavCmdInfoRover.json">src/MissionManager/UnitTest/UT-MavCmdInfoRover.json</file>
<file alias="UT-MavCmdInfoSub.json">src/MissionManager/UnitTest/UT-MavCmdInfoSub.json</file>
<file alias="UT-MavCmdInfoVTOL.json">src/MissionManager/UnitTest/UT-MavCmdInfoVTOL.json</file>
<file alias="MissionPlanner.waypoints">src/MissionManager/UnitTest/MissionPlanner.waypoints</file>
<file alias="OldFileFormat.mission">src/MissionManager/UnitTest/OldFileFormat.mission</file>
<file alias="PolygonAreaTest.kml">src/MissionManager/UnitTest/PolygonAreaTest.kml</file>
<file alias="PolygonGood.kml">src/MissionManager/UnitTest/PolygonGood.kml</file>
<file alias="PolygonMissingNode.kml">src/MissionManager/UnitTest/PolygonMissingNode.kml</file>
<file alias="PolygonBadXml.kml">src/MissionManager/UnitTest/PolygonBadXml.kml</file>
<file alias="PolygonBadCoordinatesNode.kml">src/MissionManager/UnitTest/PolygonBadCoordinatesNode.kml</file>
<file alias="MockLinkOptionsDlg.qml">src/comm/MockLinkOptionsDlg.qml</file>
<file alias="TranslationTest.json">src/qgcunittest/TranslationTest.json</file>
<file alias="TranslationTest_de_DE.ts">src/qgcunittest/TranslationTest_de_DE.ts</file>
</qresource>
<qresource prefix="/qml">
<file alias="QGroundControl/Controls/MockLinkOptionsDlg.qml">src/comm/MockLinkOptionsDlg.qml</file>
</qresource>
</RCC>

159
Vagrantfile vendored Normal file
View File

@ -0,0 +1,159 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'yaml'
current_dir = File.dirname(File.expand_path(__FILE__))
configfile = YAML.load_file("#{current_dir}/.vagrantconfig.yml")
yaml_config = configfile['configs']['dev']
env_global = [
'JOBS=4',
'SHADOW_BUILD_DIR=/tmp/shadow_build_dir',
'CODESIGN=nocodesign',
]
packages = [
'build-essential',
'fuse',
'git',
'libgstreamer-plugins-base1.0-dev',
'libgstreamer1.0-0:amd64',
'libgstreamer1.0-dev',
'libsdl2-dev',
'libudev-dev',
'speech-dispatcher',
'wget'
]
Vagrant.configure(2) do |config|
# This trick is used to prefer a VM box over docker
config.vm.provider "virtualbox"
config.vm.provider "vmware_fusion"
config.vm.box = "ubuntu/jammy64"
config.vm.provider :docker do |docker, override|
override.vm.box = "tknerr/baseimage-ubuntu-16.04"
end
config.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "6144"]
vb.customize ["modifyvm", :id, "--cpus", "1"]
vb.gui = true
end
["vmware_fusion", "vmware_workstation"].each do |p|
config.vm.provider p do |v|
v.vmx["memsize"] = "6144"
v.vmx["numvcpus"] = "1"
v.gui = true
end
end
if Vagrant.has_plugin?("vagrant-cachier")
config.cache.scope = :box
config.cache.synced_folder_opts = {
owner: "_apt"
}
end
# the "dev configuration puts the build products and a suitable
# environment into the /vagrant directory. This allows you to run
# qgroundcontrol on the host machine with:
# "cd shadow-build/release; ./qgroundcontrol-start.sh"
$config_shell = <<-'SHELL'
set -e
set -x
export %{build_env}
export JOBS=$((`cat /proc/cpuinfo | grep -c ^processor`+1))
sudo apt-get update -y
# we need this long command to keep packages (grub-pc esp.) from prompting for input
sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" dist-upgrade
sudo DEBIAN_FRONTEND=noninteractive apt-get -y -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install %{apt_pkgs} xubuntu-desktop qtcreator
sudo systemctl set-default graphical.target
echo 'Initialising submodules'
su - vagrant -c 'cd %{project_root_dir}; git submodule init && git submodule update'
# with reference to https://github.com/jurplel/install-qt-action/blob/master/src/main.ts and .github/workflows/linux_release.yml:
echo 'Installing QT'
apt-get install -y python3-pip
su - vagrant -c "pip3 install --user aqtinstall"
apt-get install -y patchelf
dir="%{qt_deps_unpack_dir}"
version="5.15.2"
host="linux"
target="desktop"
modules="qtcharts"
su - vagrant -c "rm -rf ${dir}"
su - vagrant -c "mkdir -p ${dir}"
su - vagrant -c "python3 -m aqt install-qt -O ${dir} ${host} ${target} ${version} -m ${modules}"
mkdir -p /vagrant/shadow-build
# write out a pair of scripts to make rebuilding on the VM easy:
su - vagrant -c "cat <<QMAKE >do-qmake.sh
#!/bin/bash
set -e
set -x
cd %{shadow_build_dir}
export LD_LIBRARY_PATH=%{qt_deps_lib_unpack_dir}
export PATH=%{qt_deps_bin_unpack_dir}:\$PATH
qmake -r %{pro} CONFIG+=\${CONFIG} CONFIG+=WarningsAsErrorsOn -spec %{spec}
QMAKE
"
su - vagrant -c "cat <<MAKE >do-make.sh
#!/bin/bash
set -e
set -x
cd %{shadow_build_dir}
export LD_LIBRARY_PATH=%{qt_deps_lib_unpack_dir}
export PATH=%{qt_deps_bin_unpack_dir}:\$PATH
make -j${JOBS}
MAKE
"
su - vagrant -c "chmod +x do-qmake.sh do-make.sh"
# increase the allowed number of open files (the link step takes a
# lot of open filehandles!):
echo '* soft nofile 2048' >/etc/security/limits.d/fileno.conf
# now run the scripts:
su - vagrant -c ./do-qmake.sh
su - vagrant -c ./do-make.sh
SHELL
config.vm.provision "dev", type: "shell", inline: $config_shell % {
:shadow_build_dir => yaml_config['shadow_build_dir'],
:qt_deps_tarball => yaml_config['qt_deps_tarball'],
:pro => yaml_config['pro'],
:spec => yaml_config['spec'],
:apt_pkgs => (packages).join(' '),
:build_env => env_global.select { |item| item.is_a?(String) }.join(' '),
:project_root_dir => yaml_config['project_root_dir'],
:qt_deps_unpack_parent_dir => yaml_config['qt_deps_unpack_parent_dir'],
:qt_deps_unpack_dir => yaml_config['qt_deps_unpack_dir'],
:qt_deps_bin_unpack_dir => yaml_config['qt_deps_bin_unpack_dir'],
:qt_deps_lib_unpack_dir => yaml_config['qt_deps_lib_unpack_dir'],
:qt_deps_plugins_unpack_dir => yaml_config['qt_deps_plugins_unpack_dir'],
:qt_deps_qml_unpack_dir => yaml_config['qt_deps_qml_unpack_dir'],
:qt_deps_dir => yaml_config['qt_deps_dir'],
:qt_deps_bin_dir => yaml_config['qt_deps_bin_dir'],
:qt_deps_lib_dir => yaml_config['qt_deps_lib_dir'],
:qt_deps_plugins_dir => yaml_config['qt_deps_plugins_dir'],
:qt_deps_qml_dir => yaml_config['qt_deps_qml_dir'],
}
end

View File

@ -0,0 +1,153 @@
cmake_minimum_required(VERSION 3.10)
project(VideoReceiverApp LANGUAGES C CXX)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;RelWithDebInfo;MinSizeRel;Coverage")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
include(FeatureSummary)
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
add_compile_options(-Wall -Wextra -Wno-address-of-packed-member)
endif()
# CMake build type
# Debug Release RelWithDebInfo MinSizeRel Coverage
if (NOT CMAKE_BUILD_TYPE)
# default to release with debug symbols
set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE STRING "Build type" FORCE)
endif()
set(QGC_ROOT ${CMAKE_SOURCE_DIR}/..)
# Add folder where are supportive functions
list(APPEND CMAKE_MODULE_PATH ${QGC_ROOT}/cmake)
# Configure Qt5 to get necessary variables
include(Qt5QGCConfiguration)
message(STATUS "Build Type: ${CMAKE_BUILD_TYPE}")
message(STATUS "Qt version: ${QT_VERSION}")
message(STATUS "Qt spec: ${QT_MKSPEC}")
set(COMPANY "Auterion")
set(COPYRIGHT "Copyright (c) 2020 VideoReceiverApp. All rights reserved.")
set(IDENTIFIER "labs.auterion.VideoReceiverApp")
include(Git)
message(STATUS "VideoReceiverApp version: ${APP_VERSION_STR}")
#=============================================================================
# ccache
#
option(CCACHE "Use ccache if available" ON)
find_program(CCACHE_PROGRAM ccache)
if (CCACHE AND CCACHE_PROGRAM AND NOT DEFINED ENV{CCACHE_DISABLE})
set_property(GLOBAL PROPERTY RULE_LAUNCH_COMPILE "${CCACHE_PROGRAM}")
endif()
#=============================================================================
# Compile QML
#
option(COMPILE_QML "Pre-compile QML files using the Qt Quick compiler." FALSE)
add_feature_info(COMPILE_QML COMPILE_QML "Pre-compile QML files using the Qt Quick compiler.")
if(COMPILE_QML)
find_package(Qt5QuickCompiler)
set_package_properties(Qt5QuickCompiler PROPERTIES
DESCRIPTION "Pre-compile QML files using the Qt Quick compiler."
TYPE OPTIONAL
)
endif()
#=============================================================================
# Debug QML
#
option(DEBUG_QML "Build VideoReceiverApp with QML debugging/profiling support." FALSE)
add_feature_info(DEBUG_QML DEBUG_QML "Build VideoReceiverApp with QML debugging/profiling support.")
if(DEBUG_QML)
message(STATUS "To enable the QML debugger/profiler, run with: '-qmljsdebugger=port:1234'")
add_definitions(-DQMLJSDEBUGGER)
add_definitions(-DQT_DECLARATIVE_DEBUG)
add_definitions(-DQT_QML_DEBUG)
endif()
#=============================================================================
# GStreamer
#
find_package(PkgConfig)
pkg_check_modules(GST
gstreamer-1.0>=1.14
gstreamer-video-1.0>=1.14
gstreamer-gl-1.0>=1.14
egl
)
if (GST_FOUND)
include_directories(
${GST_INCLUDE_DIRS}
)
endif()
#=============================================================================
# Qt5
#
find_package(Qt5 ${QT_VERSION}
COMPONENTS
Bluetooth
Charts
Concurrent
Core
Location
Multimedia
Network
Positioning
Quick
QuickWidgets
OpenGL
Sql
Svg
Test
TextToSpeech
Widgets
Xml
REQUIRED
HINTS
${QT_LIBRARY_HINTS}
)
# Sets the default flags for compilation and linking.
include(CompileOptions)
include_directories(
${QGC_ROOT}/src
${CMAKE_CURRENT_BINARY_DIR}
${Qt5Location_PRIVATE_INCLUDE_DIRS}
VideoReceiver
)
add_subdirectory(${QGC_ROOT}/libs/qmlglsink qmlglsink.build)
add_subdirectory(${QGC_ROOT}/src/VideoReceiver VideoReceiver.build)
set(VIDEORECIVERAPP_SOURCES main.cpp ${QGC_ROOT}/src/QGCLoggingCategory.cc)
set(VIDEORECIVERAPP_RESOURCES qml.qrc)
if(ANDROID)
add_library(VideoReceiverApp SHARED ${VIDEORECIVERAPP_SOURCES} ${VIDEORECIVERAPP_RESOURCES})
else()
add_executable(VideoReceiverApp ${VIDEORECIVERAPP_SOURCES} ${VIDEORECIVERAPP_RESOURCES})
endif()
target_link_libraries(VideoReceiverApp
PRIVATE
VideoReceiver
Qt5::Core
Qt5::Multimedia
Qt5::OpenGL
Qt5::Quick
Qt5::QuickWidgets
)

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDisplayName</key>
<string>QQmlGlSinkTest</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>NSHumanReadableCopyright</key>
<string>Open Source Flight Systems GmbH - Internal Build</string>
<key>CFBundleIconFile</key>
<string></string>
<key>CFBundleIdentifier</key>
<string>labs.auterion.VideoReceiverApp</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>0.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>UIFileSharingEnabled</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,52 @@
# VideoReceiverApp
## Application
This is a simple test application developed to make VideoReceiver library development and testing easier. It can also be used as part of CI for system tests.
## Use cases and options
Application's behaviour depends on the executable name. There are two modes - QML and console. QML mode is enabled by renaming application executable to something that starts with **Q** (for example QVideoReceiverApp). In this case **video-sink** option is not available and application always tries to use **qmlglsink** for video rendering. In regular case (executable name does not start with **Q**) **autovideosink** or **fakesink** are used, depending on options.
### Available options and required arguments
```VideoReceiverApp [options] url```
for example:
```VideoReceiverApp -d --stop-decoding 30 rtsp://127.0.0.1:8554/test```
#### Options
```-h, --help``` - displays help
```-t, --timeout <seconds>``` - specifies source timeout
```-c, --connect <attempts>``` - specifies number of connection attempts
```-d, --decode``` - enables or disables video decoding and rendering
```--no-decode``` - disables video decoding and rendering if it was enabled by default
```--stop-decoding <seconds>``` - specifies amount of seconds after which decoding should be stopped
```-r, --record <file>``` - enables record video into file
```-f, --format <format>``` - specifies recording file format, where format 0 - MKV, 1 - MOV, 2 - MP4
```--stop-recording <seconds>``` - specifies amount of seconds after which recording should be stopped
```--video-sink <sink>``` - specifies which video sink to use : 0 - autovideosink, 1 - fakesink
#### Arguments
```url``` - required, specifies video URL.
Following URLs are supported:
```rtsp://<host>:<port>/mount/point``` - usual RTSP URL
```udp://<interface>:<port>``` - H.264 over RTP/UDP
```udp265://<interface>:<port>``` - H.265 over RTP/UDP
```tsusb://<interface>:<port>``` - Taisync's forwarded H.264 byte aligned NALU stream over UDP
```tcp://<host>:<port>``` - MPEG-2 TS over TCP
```mpegts://<interface>:<port>``` - MPEG-2 TS over UDP

View File

@ -0,0 +1,82 @@
<?xml version="1.0"?>
<manifest package="labs.mavlink.VideoReceiverApp" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1" android:versionCode="100000" android:installLocation="auto">
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="labs.mavlink.VideoReceiverApp.QGLSinkActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="sensorLandscape" android:launchMode="singleTask" android:keepScreenOn="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
<action android:name="android.hardware.usb.action.USB_DEVICE_DETACHED"/>
<action android:name="android.bluetooth.device.action.ACL_CONNECTED"/>
<action android:name="android.bluetooth.device.action.ACL_DISCONNECTED"/>
<action android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED"/>
</intent-filter>
<!-- Rest of Standard Manifest -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!--
<meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/>
-->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
</activity>
</application>
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="28"/>
<!-- Needed to keep working while 'asleep' -->
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- Support devices without USB host mode since there are other connection types -->
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
<!-- Support devices without Bluetooth since there are other connection types -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
<!-- Support devices that don't have location services -->
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.usb.accessory"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
</manifest>

View File

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<manifest android:versionName="@QT_ANDROID_APP_VERSION@" package="@QT_ANDROID_APP_PACKAGE_NAME@" android:installLocation="auto" xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="@QT_ANDROID_APP_VERSION_CODE@">
<application android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtApplication">
<activity android:label="@QT_ANDROID_APP_NAME@" android:name="org.qtproject.qt5.android.bindings.QtActivity" android:screenOrientation="unspecified" android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.bundled_in_lib_resource_id" android:resource="@array/bundled_in_lib"/>
<meta-data android:name="android.app.bundled_in_assets_resource_id" android:resource="@array/bundled_in_assets"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs" android:value="-- %%INSERT_LOCAL_LIBS%% --"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Messages maps -->
<!--<meta-data android:name="android.app.ministro_not_found_msg" android:value="@string/ministro_not_found_msg"/>
<meta-data android:name="android.app.ministro_needed_msg" android:value="@string/ministro_needed_msg"/>
<meta-data android:name="android.app.fatal_error_msg" android:value="@string/fatal_error_msg"/>-->
</activity>
</application>
<supports-screens android:anyDensity="true" android:normalScreens="true" android:smallScreens="true" android:largeScreens="true"/>
<uses-sdk android:minSdkVersion="18" android:targetSdkVersion="19"/>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

View File

@ -0,0 +1,64 @@
buildscript {
repositories {
maven {
url "https://repo1.maven.org/maven2"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:1.1.0'
}
}
allprojects {
repositories {
jcenter()
}
}
apply plugin: 'com.android.application'
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
android {
/*******************************************************
* The following variables:
* - androidBuildToolsVersion,
* - androidCompileSdkVersion
* - qt5AndroidDir - holds the path to qt android files
* needed to build any Qt application
* on Android.
*
* are defined in gradle.properties file. This file is
* updated by QtCreator and androiddeployqt tools.
* Changing them manually might break the compilation!
*******************************************************/
compileSdkVersion androidCompileSdkVersion.toInteger()
buildToolsVersion androidBuildToolsVersion
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = [qt5AndroidDir + '/src', 'src', 'java']
aidl.srcDirs = [qt5AndroidDir + '/src', 'src', 'aidl']
res.srcDirs = [qt5AndroidDir + '/res', 'res']
resources.srcDirs = ['src']
renderscript.srcDirs = ['src']
assets.srcDirs = ['assets']
jniLibs.srcDirs = ['libs']
}
}
aaptOptions {
cruncherEnabled = false
}
lintOptions {
abortOnError false
}
}

Binary file not shown.

View File

@ -0,0 +1,6 @@
#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip

164
VideoReceiverApp/android/gradlew vendored Executable file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env bash
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS=""
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn ( ) {
echo "$*"
}
die ( ) {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
esac
# For Cygwin, ensure paths are in UNIX format before anything is touched.
if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
fi
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >&-
APP_HOME="`pwd -P`"
cd "$SAVED" >&-
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin, switch paths to Windows format before running java
if $cygwin ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=$((i+1))
done
case $i in
(0) set -- ;;
(1) set -- "$args0" ;;
(2) set -- "$args0" "$args1" ;;
(3) set -- "$args0" "$args1" "$args2" ;;
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
function splitJvmOpts() {
JVM_OPTS=("$@")
}
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"

90
VideoReceiverApp/android/gradlew.bat vendored Normal file
View File

@ -0,0 +1,90 @@
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS=
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto init
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto init
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:init
@rem Get command-line arguments, handling Windowz variants
if not "%OS%" == "Windows_NT" goto win9xME_args
if "%@eval[2+2]" == "4" goto 4NT_args
:win9xME_args
@rem Slurp the command line arguments.
set CMD_LINE_ARGS=
set _SKIP=2
:win9xME_args_slurp
if "x%~1" == "x" goto execute
set CMD_LINE_ARGS=%*
goto execute
:4NT_args
@rem Get arguments from the 4NT Shell from JP Software
set CMD_LINE_ARGS=%$
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,25 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<array name="qt_sources">
<item>https://download.qt-project.org/ministro/android/qt5/qt-5.4</item>
</array>
<!-- The following is handled automatically by the deployment tool. It should
not be edited manually. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="bundled_in_lib">
<!-- %%INSERT_BUNDLED_IN_LIB%% -->
</array>
<array name="bundled_in_assets">
<!-- %%INSERT_BUNDLED_IN_ASSETS%% -->
</array>
</resources>

View File

@ -0,0 +1,94 @@
package labs.mavlink.VideoReceiverApp;
/* Copyright 2013 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Written by: Mike Goza April 2014
//
// These routines interface with the Android USB Host devices for serial port communication.
// The code uses the usb-serial-for-android software library. The QGCActivity class is the
// interface to the C++ routines through jni calls. Do not change the functions without also
// changing the corresponding calls in the C++ routines or you will break the interface.
//
////////////////////////////////////////////////////////////////////////////////////////////
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Timer;
import java.util.TimerTask;
import java.io.IOException;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.widget.Toast;
import android.util.Log;
import android.os.PowerManager;
import android.os.Bundle;
import android.app.PendingIntent;
import android.view.WindowManager;
import android.os.Bundle;
import android.bluetooth.BluetoothDevice;
import org.qtproject.qt5.android.bindings.QtActivity;
import org.qtproject.qt5.android.bindings.QtApplication;
public class QGLSinkActivity extends QtActivity
{
public native void nativeInit();
// QGLSinkActivity singleton
public QGLSinkActivity() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
nativeInit();
}
@Override
public void onResume() {
super.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
public void onInit(int status) {
}
public void jniOnLoad() {
nativeInit();
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2012, Collabora Ltd.
* Author: Youness Alaoui
*
* Copyright (C) 2015, Collabora Ltd.
* Author: Justin Kim <justin.kim@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.freedesktop.gstreamer.androidmedia;
import android.hardware.Camera;
public class GstAhcCallback implements Camera.PreviewCallback,
Camera.ErrorCallback,
Camera.AutoFocusCallback {
public long mUserData;
public long mCallback;
public static native void gst_ah_camera_on_preview_frame(byte[] data, Camera camera,
long callback, long user_data);
public static native void gst_ah_camera_on_error(int error, Camera camera,
long callback, long user_data);
public static native void gst_ah_camera_on_auto_focus(boolean success, Camera camera,
long callback, long user_data);
public GstAhcCallback(long callback, long user_data) {
mCallback = callback;
mUserData = user_data;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
gst_ah_camera_on_preview_frame(data, camera, mCallback, mUserData);
}
@Override
public void onError(int error, Camera camera) {
gst_ah_camera_on_error(error, camera, mCallback, mUserData);
}
@Override
public void onAutoFocus(boolean success, Camera camera) {
gst_ah_camera_on_auto_focus(success, camera, mCallback, mUserData);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2016 SurroundIO
* Author: Martin Kelly <martin@surround.io>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.freedesktop.gstreamer.androidmedia;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class GstAhsCallback implements SensorEventListener {
public long mUserData;
public long mSensorCallback;
public long mAccuracyCallback;
public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event,
long callback, long user_data);
public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy,
long callback, long user_data);
public GstAhsCallback(long sensor_callback,
long accuracy_callback, long user_data) {
mSensorCallback = sensor_callback;
mAccuracyCallback = accuracy_callback;
mUserData = user_data;
}
@Override
public void onSensorChanged(SensorEvent event) {
gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
gst_ah_sensor_on_accuracy_changed(sensor, accuracy,
mAccuracyCallback, mUserData);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2015, Collabora Ltd.
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.freedesktop.gstreamer.androidmedia;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
public class GstAmcOnFrameAvailableListener implements OnFrameAvailableListener
{
private long context = 0;
public synchronized void onFrameAvailable (SurfaceTexture surfaceTexture) {
native_onFrameAvailable(context, surfaceTexture);
}
public synchronized long getContext () {
return context;
}
public synchronized void setContext (long c) {
context = c;
}
private native void native_onFrameAvailable (long context, SurfaceTexture surfaceTexture);
}

496
VideoReceiverApp/main.cpp Normal file
View File

@ -0,0 +1,496 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QQuickItem>
#include <QRunnable>
#include <QCommandLineParser>
#include <QTimer>
#include <gst/gst.h>
#include "QGCLoggingCategory.h"
QGC_LOGGING_CATEGORY(AppLog, "VideoReceiverApp")
#if defined(__android__)
#include <QtAndroidExtras>
#include <jni.h>
#include <android/log.h>
static jobject _class_loader = nullptr;
static jobject _context = nullptr;
extern "C" {
void gst_amc_jni_set_java_vm(JavaVM *java_vm);
jobject gst_android_get_application_class_loader(void) {
return _class_loader;
}
}
static void
gst_android_init(JNIEnv* env, jobject context)
{
jobject class_loader = nullptr;
jclass context_cls = env->GetObjectClass(context);
if (!context_cls) {
return;
}
jmethodID get_class_loader_id = env->GetMethodID(context_cls, "getClassLoader", "()Ljava/lang/ClassLoader;");
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
class_loader = env->CallObjectMethod(context, get_class_loader_id);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
return;
}
_context = env->NewGlobalRef(context);
_class_loader = env->NewGlobalRef(class_loader);
}
static const char kJniClassName[] {"labs/mavlink/VideoReceiverApp/QGLSinkActivity"};
static void setNativeMethods(void)
{
JNINativeMethod javaMethods[] {
{"nativeInit", "()V", reinterpret_cast<void *>(gst_android_init)}
};
QAndroidJniEnvironment jniEnv;
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionDescribe();
jniEnv->ExceptionClear();
}
jclass objectClass = jniEnv->FindClass(kJniClassName);
if (!objectClass) {
qWarning() << "Couldn't find class:" << kJniClassName;
return;
}
jint val = jniEnv->RegisterNatives(objectClass, javaMethods, sizeof(javaMethods) / sizeof(javaMethods[0]));
if (val < 0) {
qWarning() << "Error registering methods: " << val;
} else {
qDebug() << "Main Native Functions Registered";
}
if (jniEnv->ExceptionCheck()) {
jniEnv->ExceptionDescribe();
jniEnv->ExceptionClear();
}
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
Q_UNUSED(reserved);
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
setNativeMethods();
gst_amc_jni_set_java_vm(vm);
return JNI_VERSION_1_6;
}
#endif
#include <GStreamer.h>
#include <VideoReceiver.h>
class VideoReceiverApp : public QRunnable
{
public:
VideoReceiverApp(QCoreApplication& app, bool qmlAllowed)
: _app(app)
, _qmlAllowed(qmlAllowed)
{}
void run();
int exec();
void startStreaming();
void startDecoding();
void startRecording();
protected:
void _dispatch(std::function<void()> code);
private:
QCoreApplication& _app;
bool _qmlAllowed;
VideoReceiver* _receiver = nullptr;
QQuickWindow* _window = nullptr;
QQuickItem* _widget = nullptr;
void* _videoSink = nullptr;
QString _url;
unsigned _timeout = 5;
unsigned _connect = 1;
bool _decode = true;
unsigned _stopDecodingAfter = 0;
bool _record = false;
QString _videoFile;
unsigned int _fileFormat = VideoReceiver::FILE_FORMAT_MIN;
unsigned _stopRecordingAfter = 15;
bool _useFakeSink = false;
bool _streaming = false;
bool _decoding = false;
bool _recording = false;
};
void
VideoReceiverApp::run()
{
if((_videoSink = GStreamer::createVideoSink(nullptr, _widget)) == nullptr) {
qCDebug(AppLog) << "createVideoSink failed";
return;
}
_receiver->startDecoding(_videoSink);
}
int
VideoReceiverApp::exec()
{
QCommandLineParser parser;
parser.addHelpOption();
parser.addPositionalArgument("url",
QCoreApplication::translate("main", "Source URL."));
QCommandLineOption timeoutOption(QStringList() << "t" << "timeout",
QCoreApplication::translate("main", "Source timeout."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(timeoutOption);
QCommandLineOption connectOption(QStringList() << "c" << "connect",
QCoreApplication::translate("main", "Number of connection attempts."),
QCoreApplication::translate("main", "attempts"));
parser.addOption(connectOption);
QCommandLineOption decodeOption(QStringList() << "d" << "decode",
QCoreApplication::translate("main", "Decode and render video."));
parser.addOption(decodeOption);
QCommandLineOption noDecodeOption("no-decode",
QCoreApplication::translate("main", "Don't decode and render video."));
parser.addOption(noDecodeOption);
QCommandLineOption stopDecodingOption("stop-decoding",
QCoreApplication::translate("main", "Stop decoding after time."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(stopDecodingOption);
QCommandLineOption recordOption(QStringList() << "r" << "record",
QCoreApplication::translate("main", "Record video."),
QGuiApplication::translate("main", "file"));
parser.addOption(recordOption);
QCommandLineOption formatOption(QStringList() << "f" << "format",
QCoreApplication::translate("main", "File format."),
QCoreApplication::translate("main", "format"));
parser.addOption(formatOption);
QCommandLineOption stopRecordingOption("stop-recording",
QCoreApplication::translate("main", "Stop recording after time."),
QCoreApplication::translate("main", "seconds"));
parser.addOption(stopRecordingOption);
QCommandLineOption videoSinkOption("video-sink",
QCoreApplication::translate("main", "Use video sink: 0 - autovideosink, 1 - fakesink"),
QCoreApplication::translate("main", "sink"));
if (!_qmlAllowed) {
parser.addOption(videoSinkOption);
}
parser.process(_app);
const QStringList args = parser.positionalArguments();
if (args.size() != 1) {
parser.showHelp(0);
}
_url = args.at(0);
if (parser.isSet(timeoutOption)) {
_timeout = parser.value(timeoutOption).toUInt();
}
if (parser.isSet(connectOption)) {
_connect = parser.value(connectOption).toUInt();
}
if (parser.isSet(decodeOption) && parser.isSet(noDecodeOption)) {
parser.showHelp(0);
}
if (parser.isSet(decodeOption)) {
_decode = true;
}
if (parser.isSet(noDecodeOption)) {
_decode = false;
}
if (_decode && parser.isSet(stopDecodingOption)) {
_stopDecodingAfter = parser.value(stopDecodingOption).toUInt();
}
if (parser.isSet(recordOption)) {
_record = true;
_videoFile = parser.value(recordOption);
}
if (parser.isSet(formatOption)) {
_fileFormat += parser.value(formatOption).toUInt();
}
if (_record && parser.isSet(stopRecordingOption)) {
_stopRecordingAfter = parser.value(stopRecordingOption).toUInt();
}
if (parser.isSet(videoSinkOption)) {
_useFakeSink = parser.value(videoSinkOption).toUInt() > 0;
}
_receiver = GStreamer::createVideoReceiver(nullptr);
QQmlApplicationEngine engine;
if (_decode && _qmlAllowed) {
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
_window = static_cast<QQuickWindow*>(engine.rootObjects().first());
Q_ASSERT(_window != nullptr);
_widget = _window->findChild<QQuickItem*>("videoItem");
Q_ASSERT(_widget != nullptr);
}
startStreaming();
QObject::connect(_receiver, &VideoReceiver::timeout, [](){
qCDebug(AppLog) << "Streaming timeout";
});
QObject::connect(_receiver, &VideoReceiver::streamingChanged, [this](bool active){
_streaming = active;
if (_streaming) {
qCDebug(AppLog) << "Streaming started";
} else {
qCDebug(AppLog) << "Streaming stopped";
}
});
QObject::connect(_receiver, &VideoReceiver::decodingChanged, [this](bool active){
_decoding = active;
if (_decoding) {
qCDebug(AppLog) << "Decoding started";
} else {
qCDebug(AppLog) << "Decoding stopped";
if (_streaming) {
if (!_recording) {
_dispatch([this](){
_receiver->stop();
});
}
}
}
});
QObject::connect(_receiver, &VideoReceiver::recordingChanged, [this](bool active){
_recording = active;
if (_recording) {
qCDebug(AppLog) << "Recording started";
} else {
qCDebug(AppLog) << "Recording stopped";
if (_streaming) {
if (!_decoding) {
_dispatch([this](){
_receiver->stop();
});
}
}
}
});
QObject::connect(_receiver, &VideoReceiver::onStartComplete, [this](VideoReceiver::STATUS status){
if (status != VideoReceiver::STATUS_OK) {
qCDebug(AppLog) << "Video receiver start failed";
_dispatch([this](){
if (--_connect > 0) {
qCDebug(AppLog) << "Restarting ...";
_dispatch([this](){
startStreaming();
});
} else {
qCDebug(AppLog) << "Closing...";
delete _receiver;
_app.exit();
}
});
} else {
qCDebug(AppLog) << "Video receiver started";
}
});
QObject::connect(_receiver, &VideoReceiver::onStopComplete, [this](VideoReceiver::STATUS ){
qCDebug(AppLog) << "Video receiver stopped";
_dispatch([this](){
if (--_connect > 0) {
qCDebug(AppLog) << "Restarting ...";
_dispatch([this](){
startStreaming();
});
} else {
qCDebug(AppLog) << "Closing...";
delete _receiver;
_app.exit();
}
});
});
return _app.exec();
}
void
VideoReceiverApp::startStreaming()
{
_receiver->start(_url, _timeout);
if (_decode) {
startDecoding();
}
if (_record) {
startRecording();
}
}
void
VideoReceiverApp::startDecoding()
{
if (_qmlAllowed) {
_window->scheduleRenderJob(this, QQuickWindow::BeforeSynchronizingStage);
} else {
if (_videoSink == nullptr) {
if ((_videoSink = gst_element_factory_make(_useFakeSink ? "fakesink" : "autovideosink", nullptr)) == nullptr) {
qCDebug(AppLog) << "Failed to create video sink";
return;
}
}
_receiver->startDecoding(_videoSink);
}
if (_stopDecodingAfter > 0) {
unsigned connect = _connect;
QTimer::singleShot(_stopDecodingAfter * 1000, Qt::PreciseTimer, [this, connect](){
if (connect != _connect) {
return;
}
_receiver->stopDecoding();
});
}
}
void
VideoReceiverApp::startRecording()
{
_receiver->startRecording(_videoFile, static_cast<VideoReceiver::FILE_FORMAT>(_fileFormat));
if (_stopRecordingAfter > 0) {
unsigned connect = _connect;
QTimer::singleShot(_stopRecordingAfter * 1000, [this, connect](){
if (connect != _connect) {
return;
}
_receiver->stopRecording();
});
}
}
void
VideoReceiverApp::_dispatch(std::function<void()> code)
{
QTimer* timer = new QTimer();
timer->moveToThread(qApp->thread());
timer->setSingleShot(true);
QObject::connect(timer, &QTimer::timeout, [=](){
code();
timer->deleteLater();
});
QMetaObject::invokeMethod(timer, "start", Qt::QueuedConnection, Q_ARG(int, 0));
}
static bool isQtApp(const char* app)
{
const char* s;
#if defined(Q_OS_WIN)
if ((s = strrchr(app, '\\')) != nullptr) {
#else
if ((s = strrchr(app, '/')) != nullptr) {
#endif
s += 1;
} else {
s = app;
}
return s[0] == 'Q' || s[0] == 'q';
}
int main(int argc, char *argv[])
{
if (argc < 1) {
return 0;
}
GStreamer::initialize(argc, argv, 3);
if (isQtApp(argv[0])) {
QGuiApplication app(argc, argv);
VideoReceiverApp videoApp(app, true);
return videoApp.exec();
} else {
QCoreApplication app(argc, argv);
VideoReceiverApp videoApp(app, false);
return videoApp.exec();
}
}

23
VideoReceiverApp/main.qml Normal file
View File

@ -0,0 +1,23 @@
import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Layouts 1.3
import org.freedesktop.gstreamer.GLVideoItem 1.0
Window {
visible: true
width: 640
height: 480
title: qsTr("VideoReceiverApp")
RowLayout {
anchors.fill: parent
spacing: 0
GstGLVideoItem {
id: video
objectName: "videoItem"
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}

5
VideoReceiverApp/qml.qrc Normal file
View File

@ -0,0 +1,5 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
</qresource>
</RCC>

110
android.pri Normal file
View File

@ -0,0 +1,110 @@
QT += androidextras
include($$PWD/libs/qtandroidserialport/src/qtandroidserialport.pri)
ANDROID_PACKAGE_SOURCE_DIR = $$OUT_PWD/ANDROID_PACKAGE_SOURCE_DIR # Tells Qt location of package files for build
ANDROID_PACKAGE_QGC_SOURCE_DIR = $$PWD/android # Original location of QGC package files
ANDROID_PACKAGE_CUSTOM_SOURCE_DIR = $$PWD/custom/android # Original location for custom build override package files
# We always move the package files to the ANDROID_PACKAGE_SOURCE_DIR build dir so we can modify the manifest as needed
android_source_dir_target.target = $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
android_source_dir_target.commands = \
$$QMAKE_MKDIR $$ANDROID_PACKAGE_SOURCE_DIR && \
$$QMAKE_COPY_DIR $$ANDROID_PACKAGE_QGC_SOURCE_DIR/* $$ANDROID_PACKAGE_SOURCE_DIR
PRE_TARGETDEPS += $$android_source_dir_target.target
QMAKE_EXTRA_TARGETS += android_source_dir_target
exists($$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/AndroidManifest.xml) {
android_source_dir_target.depends = $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/AndroidManifest.xml
} else {
android_source_dir_target.depends = $$ANDROID_PACKAGE_QGC_SOURCE_DIR/AndroidManifest.xml
}
# Custom builds can override android package file
exists($$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR) {
message("Merging$$ $$ANDROID_PACKAGE_QGC_SOURCE_DIR and $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR to $$ANDROID_PACKAGE_SOURCE_DIR")
android_source_dir_target.commands = $$android_source_dir_target.commands && \
$$QMAKE_COPY_DIR $$ANDROID_PACKAGE_CUSTOM_SOURCE_DIR/* $$ANDROID_PACKAGE_SOURCE_DIR && \
$$QMAKE_STREAM_EDITOR -i \"s/package=\\\"org.mavlink.qgroundcontrol\\\"/package=\\\"$$QGC_ANDROID_PACKAGE\\\"/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
}
# Insert package name into manifest file
android_source_dir_target.commands = $$android_source_dir_target.commands && \
$$QMAKE_STREAM_EDITOR -i \"s/%%QGC_INSERT_PACKAGE_NAME%%/$$QGC_ANDROID_PACKAGE/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
# Update manifest activity intent filter as needed
QGC_INSERT_ACTIVITY_INTENT_FILTER = ""
AndroidHomeApp {
# QGC is the android home application
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<category android:name=\\\"android.intent.category.HOME\\\"\\\/>\r\n<category android:name=\\\"android.intent.category.DEFAULT\\\"\\\/>"
}
!contains(DEFINES, NO_SERIAL_LINK) {
# Add usb device support
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<action android:name=\\\"android.hardware.usb.action.USB_DEVICE_ATTACHED\\\"\\\/>\r\n<action android:name=\\\"android.hardware.usb.action.USB_DEVICE_DETACHED\\\"\\\/>\r\n<action android:name=\\\"android.hardware.usb.action.USB_ACCESSORY_ATTACHED\\\"\\\/>"
}
contains(DEFINES, QGC_ENABLE_BLUETOOTH) {
QGC_INSERT_ACTIVITY_INTENT_FILTER = $$QGC_INSERT_ACTIVITY_INTENT_FILTER "\r\n<action android:name=\\\"android.bluetooth.device.action.ACL_CONNECTED\\\"\\\/>\r\n<action android:name=\\\"android.bluetooth.device.action.ACL_DISCONNECTED\\\"\\\/>"
}
android_source_dir_target.commands = $$android_source_dir_target.commands && \
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_INTENT_FILTER -->/$$QGC_INSERT_ACTIVITY_INTENT_FILTER/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
# Update manifest activity meta data as needed
contains(DEFINES, NO_SERIAL_LINK) {
# No need to add anything to manifest
android_source_dir_target.commands = $$android_source_dir_target.commands && \
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->//\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
} else {
# Updates the manifest for usb device support
android_source_dir_target.commands = $$android_source_dir_target.commands && \
$$QMAKE_STREAM_EDITOR -i \"s/<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->/<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_DEVICE_ATTACHED\\\"\\\/>\r\n<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_DEVICE_DETACHED\\\"\\\/>\r\n<meta-data android:resource=\\\"@xml\\\/device_filter\\\" android:name=\\\"android.hardware.usb.action.USB_ACCESSORY_ATTACHED\\\"\\\/>/\" $$ANDROID_PACKAGE_SOURCE_DIR/AndroidManifest.xml
}
# OTHER_FILES makes the specified files be visible in Qt Creator for editing
exists($$PWD/custom/android/AndroidManifest.xml) {
OTHER_FILES += \
$$PWD/custom/android/AndroidManifest.xml
} else {
OTHER_FILES += \
$$PWD/android/AndroidManifest.xml
}
OTHER_FILES += \
$$PWD/android/res/xml/device_filter.xml \
$$PWD/android/src/com/hoho/android/usbserial/driver/CdcAcmSerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/CommonUsbSerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/Cp2102SerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/FtdiSerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/ProlificSerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbId.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialDriver.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialProber.java \
$$PWD/android/src/com/hoho/android/usbserial/driver/UsbSerialRuntimeException.java \
$$PWD/android/src/org/mavlink/qgroundcontrol/QGCActivity.java \
$$PWD/android/src/org/mavlink/qgroundcontrol/UsbIoManager.java \
$$PWD/android/src/org/mavlink/qgroundcontrol/TaiSync.java \
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAhcCallback.java \
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAhsCallback.java \
$$PWD/android/src/org/freedesktop/gstreamer/androidmedia/GstAmcOnFrameAvailableListener.java
DISTFILES += \
$$PWD/android/gradle/wrapper/gradle-wrapper.jar \
$$PWD/android/gradlew \
$$PWD/android/res/values/libs.xml \
$$PWD/android/build.gradle \
$$PWD/android/gradle/wrapper/gradle-wrapper.properties \
$$PWD/android/gradlew.bat
SOURCES += \
$$PWD/android/src/AndroidInterface.cc
HEADERS += \
$$PWD/android/src/AndroidInterface.h
INCLUDEPATH += \
$$PWD/android/src

104
android/AndroidManifest.xml Normal file
View File

@ -0,0 +1,104 @@
<?xml version="1.0"?>
<manifest package="%%QGC_INSERT_PACKAGE_NAME%%" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="-- %%INSERT_VERSION_NAME%% --" android:versionCode="-- %%INSERT_VERSION_CODE%% --" android:installLocation="auto">
<!-- The following comment will be replaced upon deployment with default permissions based on the dependencies of the application.
Remove the comment if you do not require these default permissions. -->
<!-- %%INSERT_PERMISSIONS -->
<!-- The following comment will be replaced upon deployment with default features based on the dependencies of the application.
Remove the comment if you do not require these default features. -->
<!-- %%INSERT_FEATURES -->
<supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/>
<application android:hardwareAccelerated="true" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="-- %%INSERT_APP_NAME%% --" android:extractNativeLibs="true" android:icon="@drawable/icon">
<activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|layoutDirection|locale|fontScale|keyboard|keyboardHidden|navigation|mcc|mnc|density" android:name="org.mavlink.qgroundcontrol.QGCActivity" android:label="-- %%INSERT_APP_NAME%% --" android:screenOrientation="sensorLandscape" android:launchMode="singleTask" android:keepScreenOn="true">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<!-- %%QGC_INSERT_ACTIVITY_INTENT_FILTER -->
</intent-filter>
<!-- %%QGC_INSERT_ACTIVITY_META_DATA -->
<!-- Application arguments -->
<!-- meta-data android:name="android.app.arguments" android:value="arg1 arg2 arg3"/ -->
<!-- Application arguments -->
<meta-data android:name="android.app.lib_name" android:value="-- %%INSERT_APP_LIB_NAME%% --"/>
<meta-data android:name="android.app.qt_sources_resource_id" android:resource="@array/qt_sources"/>
<meta-data android:name="android.app.repository" android:value="default"/>
<meta-data android:name="android.app.qt_libs_resource_id" android:resource="@array/qt_libs"/>
<meta-data android:name="android.app.bundled_libs_resource_id" android:resource="@array/bundled_libs"/>
<!-- Deploy Qt libs as part of package -->
<meta-data android:name="android.app.bundle_local_qt_libs" android:value="-- %%BUNDLE_LOCAL_QT_LIBS%% --"/>
<!-- Run with local libs -->
<meta-data android:name="android.app.use_local_qt_libs" android:value="-- %%USE_LOCAL_QT_LIBS%% --"/>
<meta-data android:name="android.app.libs_prefix" android:value="/data/local/tmp/qt/"/>
<meta-data android:name="android.app.load_local_libs_resource_id" android:resource="@array/load_local_libs"/>
<meta-data android:name="android.app.load_local_jars" android:value="-- %%INSERT_LOCAL_JARS%% --"/>
<meta-data android:name="android.app.static_init_classes" android:value="-- %%INSERT_INIT_CLASSES%% --"/>
<!-- Used to specify custom system library path to run with local system libs -->
<!-- <meta-data android:name="android.app.system_libs_prefix" android:value="/system/lib/"/> -->
<!-- Messages maps -->
<meta-data android:value="@string/ministro_not_found_msg" android:name="android.app.ministro_not_found_msg"/>
<meta-data android:value="@string/ministro_needed_msg" android:name="android.app.ministro_needed_msg"/>
<meta-data android:value="@string/fatal_error_msg" android:name="android.app.fatal_error_msg"/>
<meta-data android:value="@string/unsupported_android_version" android:name="android.app.unsupported_android_version"/>
<!-- Messages maps -->
<!-- Splash screen -->
<!-- Orientation-specific (portrait/landscape) data is checked first. If not available for current orientation,
then android.app.splash_screen_drawable. For best results, use together with splash_screen_sticky and
use hideSplashScreen() with a fade-out animation from Qt Android Extras to hide the splash screen when you
are done populating your window with content. -->
<!-- meta-data android:name="android.app.splash_screen_drawable_portrait" android:resource="@drawable/logo_portrait" / -->
<!-- meta-data android:name="android.app.splash_screen_drawable_landscape" android:resource="@drawable/logo_landscape" / -->
<!-- meta-data android:name="android.app.splash_screen_drawable" android:resource="@drawable/logo"/ -->
<!-- meta-data android:name="android.app.splash_screen_sticky" android:value="true"/ -->
<!-- Splash screen -->
<!-- Background running -->
<!-- Warning: changing this value to true may cause unexpected crashes if the
application still try to draw after
"applicationStateChanged(Qt::ApplicationSuspended)"
signal is sent! -->
<meta-data android:name="android.app.background_running" android:value="false"/>
<!-- Background running -->
<!-- auto screen scale factor -->
<meta-data android:name="android.app.auto_screen_scale_factor" android:value="false"/>
<!-- auto screen scale factor -->
<!-- extract android style -->
<!-- available android:values :
* default - In most cases this will be the same as "full", but it can also be something else if needed, e.g., for compatibility reasons
* full - useful QWidget & Quick Controls 1 apps
* minimal - useful for Quick Controls 2 apps, it is much faster than "full"
* none - useful for apps that don't use any of the above Qt modules
-->
<meta-data android:name="android.app.extract_android_style" android:value="default"/>
<!-- extract android style -->
</activity>
<!-- For adding service(s) please check: https://wiki.qt.io/AndroidServices -->
</application>
<uses-sdk android:targetSdkVersion="30"/>
<!-- Needed to keep working while 'asleep' -->
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<!-- Need MulticastLock to receive broadcast UDP packets -->
<uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
<!-- Support devices without USB host mode since there are other connection types -->
<uses-feature android:name="android.hardware.usb.host" android:required="false"/>
<!-- Support devices without Bluetooth since there are other connection types -->
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
<!-- Support devices that don't have location services -->
<uses-feature android:name="android.hardware.location.gps" android:required="false"/>
<uses-feature android:name="android.hardware.location.network" android:required="false"/>
<uses-feature android:name="android.hardware.location" android:required="false"/>
<uses-feature android:name="android.hardware.usb.accessory" android:required="false"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_INTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
</manifest>

Binary file not shown.

After

Width:  |  Height:  |  Size: 173 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 787 KiB

BIN
android/GooglePlayScreenShot1.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

BIN
android/GooglePlayScreenShot2.jpg Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

Binary file not shown.

BIN
android/libs/d2xx.jar Executable file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,22 @@
<?xml version='1.0' encoding='utf-8'?>
<resources>
<array name="qt_sources">
<item>https://download.qt.io/ministro/android/qt5/qt-5.14</item>
</array>
<!-- The following is handled automatically by the deployment tool. It should
not be edited manually. -->
<array name="bundled_libs">
<!-- %%INSERT_EXTRA_LIBS%% -->
</array>
<array name="qt_libs">
<!-- %%INSERT_QT_LIBS%% -->
</array>
<array name="load_local_libs">
<!-- %%INSERT_LOCAL_LIBS%% -->
</array>
</resources>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Allow anything connected -->
<usb-device />
<usb-accessory model="android.usbaoa" manufacturer="taisync" version="1.0"/>
</resources>

View File

@ -0,0 +1,36 @@
/****************************************************************************
*
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#include <QtAndroidExtras/QtAndroidExtras>
#include <QtAndroidExtras/QAndroidJniObject>
#include "QGCApplication.h"
#include "AndroidInterface.h"
#include <QAndroidJniObject>
#include <QtAndroid>
QString AndroidInterface::getSDCardPath()
{
QAndroidJniObject value = QAndroidJniObject::callStaticObjectMethod("org/mavlink/qgroundcontrol/QGCActivity", "getSDCardPath",
"()Ljava/lang/String;");
QString sdCardPath = value.toString();
QString readPermission("android.permission.READ_EXTERNAL_STORAGE");
QString writePermission("android.permission.WRITE_EXTERNAL_STORAGE");
if (QtAndroid::checkPermission(readPermission) == QtAndroid::PermissionResult::Denied ||
QtAndroid::checkPermission(writePermission) == QtAndroid::PermissionResult::Denied) {
QtAndroid::PermissionResultMap resultHash = QtAndroid::requestPermissionsSync(QStringList({ readPermission, writePermission }));
if (resultHash[readPermission] == QtAndroid::PermissionResult::Denied ||
resultHash[writePermission] == QtAndroid::PermissionResult::Denied) {
return QString();
}
}
return sdCardPath;
}

View File

@ -0,0 +1,21 @@
/****************************************************************************
*
* Copyright (C) 2018 Pinecone Inc. All rights reserved.
*
* QGroundControl is licensed according to the terms in the file
* COPYING.md in the root of the source code directory.
*
****************************************************************************/
#pragma once
#include <QObject>
#include <jni.h>
#include <QtCore/private/qjni_p.h>
#include <QtCore/private/qjnihelpers_p.h>
class AndroidInterface
{
public:
static QString getSDCardPath();
};

View File

@ -0,0 +1,322 @@
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* USB CDC/ACM serial driver implementation.
*
* @author mike wakerly (opensource@hoho.com)
* @see <a
* href="http://www.usb.org/developers/devclass_docs/usbcdc11.pdf">Universal
* Serial Bus Class Definitions for Communication Devices, v1.1</a>
*/
public class CdcAcmSerialDriver extends CommonUsbSerialDriver {
private final String TAG = CdcAcmSerialDriver.class.getSimpleName();
private UsbInterface mControlInterface;
private UsbInterface mDataInterface;
private UsbEndpoint mControlEndpoint;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
private boolean mRts = false;
private boolean mDtr = false;
private static final int USB_RECIP_INTERFACE = 0x01;
private static final int USB_RT_ACM = UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
private static final int SET_LINE_CODING = 0x20; // USB CDC 1.1 section 6.2
private static final int GET_LINE_CODING = 0x21;
private static final int SET_CONTROL_LINE_STATE = 0x22;
private static final int SEND_BREAK = 0x23;
public CdcAcmSerialDriver(UsbDevice device) {
super(device);
}
@Override
public void open() throws IOException {
Log.d(TAG, "device " + mDevice);
mControlInterface = null;
mDataInterface = null;
mWriteEndpoint = null;
mReadEndpoint = null;
// locate all needed interfaces
for(int i = 0; i < mDevice.getInterfaceCount();i++){
UsbInterface iface = mDevice.getInterface(i);
switch(iface.getInterfaceClass()){
case UsbConstants.USB_CLASS_COMM:
mControlInterface = iface;
Log.d(TAG, "control iface=" + iface);
break;
case UsbConstants.USB_CLASS_CDC_DATA:
mDataInterface = iface;
Log.d(TAG, "data iface=" + iface);
break;
default:
Log.d(TAG, "skipping iface=" + iface);
break;
}
}
// failback to the old way
if(mControlInterface == null) {
mControlInterface = mDevice.getInterface(0);
Log.d(TAG, "Failback: Control iface=" + mControlInterface);
}
if (!mConnection.claimInterface(mControlInterface, true)) {
throw new IOException("Could not claim control interface.");
}
mControlEndpoint = mControlInterface.getEndpoint(0);
Log.d(TAG, "Control endpoint: " + mControlEndpoint);
if(mDataInterface == null) {
mDataInterface = mDevice.getInterface(1);
Log.d(TAG, "Failback: data iface=" + mDataInterface);
}
if (!mConnection.claimInterface(mDataInterface, true)) {
throw new IOException("Could not claim data interface.");
}
for(int i = 0; i < mDataInterface.getEndpointCount(); i++) {
UsbEndpoint endpoint = mDataInterface.getEndpoint(i);
switch (endpoint.getDirection()) {
case UsbConstants.USB_DIR_OUT:
mWriteEndpoint = endpoint;
Log.d(TAG, "Write endpoint: " + mWriteEndpoint);
break;
case UsbConstants.USB_DIR_IN:
mReadEndpoint = endpoint;
Log.d(TAG, "Read endpoint: " + mReadEndpoint);
break;
}
}
if(mReadEndpoint == null || mWriteEndpoint == null){
// failback to the old method
mReadEndpoint = mDataInterface.getEndpoint(0);
Log.d(TAG, "Read endpoint direction: " + mReadEndpoint.getDirection());
mWriteEndpoint = mDataInterface.getEndpoint(1);
Log.d(TAG, "Write endpoint direction: " + mWriteEndpoint.getDirection());
}
}
private int sendAcmControlMessage(int request, int value, byte[] buf) {
return mConnection.controlTransfer(
USB_RT_ACM, request, value, 0, buf, buf != null ? buf.length : 0, 5000);
}
@Override
public void close() throws IOException {
mConnection.close();
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
}
return numBytesRead;
}
@Override
public int write(byte[] src, int timeoutMillis) throws IOException {
// TODO(mikey): Nearly identical to FtdiSerial write. Refactor.
int offset = 0;
while (offset < src.length) {
final int writeLength;
final int amtWritten;
synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) {
writeBuffer = src;
} else {
// bulkTransfer does not support offsets, make a copy.
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
timeoutMillis);
}
if (amtWritten <= 0) {
throw new IOException("Error writing " + writeLength
+ " bytes at offset " + offset + " length=" + src.length);
}
//Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
offset += amtWritten;
}
return offset;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) {
byte stopBitsByte;
switch (stopBits) {
case STOPBITS_1: stopBitsByte = 0; break;
case STOPBITS_1_5: stopBitsByte = 1; break;
case STOPBITS_2: stopBitsByte = 2; break;
default: throw new IllegalArgumentException("Bad value for stopBits: " + stopBits);
}
byte parityBitesByte;
switch (parity) {
case PARITY_NONE: parityBitesByte = 0; break;
case PARITY_ODD: parityBitesByte = 1; break;
case PARITY_EVEN: parityBitesByte = 2; break;
case PARITY_MARK: parityBitesByte = 3; break;
case PARITY_SPACE: parityBitesByte = 4; break;
default: throw new IllegalArgumentException("Bad value for parity: " + parity);
}
byte[] msg = {
(byte) ( baudRate & 0xff),
(byte) ((baudRate >> 8 ) & 0xff),
(byte) ((baudRate >> 16) & 0xff),
(byte) ((baudRate >> 24) & 0xff),
stopBitsByte,
parityBitesByte,
(byte) dataBits};
sendAcmControlMessage(SET_LINE_CODING, 0, msg);
}
@Override
public boolean getCD() throws IOException {
return false; // TODO
}
@Override
public boolean getCTS() throws IOException {
return false; // TODO
}
@Override
public boolean getDSR() throws IOException {
return false; // TODO
}
@Override
public boolean getDTR() throws IOException {
return mDtr;
}
@Override
public void setDTR(boolean value) throws IOException {
mDtr = value;
setDtrRts();
}
@Override
public boolean getRI() throws IOException {
return false; // TODO
}
@Override
public boolean getRTS() throws IOException {
return mRts;
}
@Override
public void setRTS(boolean value) throws IOException {
mRts = value;
setDtrRts();
}
private void setDtrRts() {
int value = (mRts ? 0x2 : 0) | (mDtr ? 0x1 : 0);
sendAcmControlMessage(SET_CONTROL_LINE_STATE, value, null);
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUINO),
new int[] {
UsbId.ARDUINO_UNO,
UsbId.ARDUINO_UNO_R3,
UsbId.ARDUINO_MEGA_2560,
UsbId.ARDUINO_MEGA_2560_R3,
UsbId.ARDUINO_SERIAL_ADAPTER,
UsbId.ARDUINO_SERIAL_ADAPTER_R3,
UsbId.ARDUINO_MEGA_ADK,
UsbId.ARDUINO_MEGA_ADK_R3,
UsbId.ARDUINO_LEONARDO,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_VAN_OOIJEN_TECH),
new int[] {
UsbId.VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ATMEL),
new int[] {
UsbId.ATMEL_LUFA_CDC_DEMO_APP,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_LEAFLABS),
new int[] {
UsbId.LEAFLABS_MAPLE,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4),
new int[] {
UsbId.DEVICE_PX4FMU,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_UBLOX),
new int[] {
UsbId.DEVICE_UBLOX_5,
UsbId.DEVICE_UBLOX_6,
UsbId.DEVICE_UBLOX_7,
UsbId.DEVICE_UBLOX_8,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_OPENPILOT),
new int[] {
UsbId.DEVICE_CC3D,
UsbId.DEVICE_REVOLUTION,
UsbId.DEVICE_SPARKY2,
UsbId.DEVICE_OPLINK,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUPILOT_CHIBIOS1),
new int[] {
UsbId.DEVICE_ARDUPILOT_CHIBIOS,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_ARDUPILOT_CHIBIOS2),
new int[] {
UsbId.DEVICE_ARDUPILOT_CHIBIOS,
});
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_DRAGONLINK),
new int[] {
UsbId.DEVICE_DRAGONLINK,
});
return supportedDevices;
}
}

View File

@ -0,0 +1,172 @@
/* Copyright 2013 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import java.io.IOException;
/**
* A base class shared by several driver implementations.
*
* @author mike wakerly (opensource@hoho.com)
*/
abstract class CommonUsbSerialDriver implements UsbSerialDriver {
public static final int DEFAULT_READ_BUFFER_SIZE = 16 * 1024;
public static final int DEFAULT_WRITE_BUFFER_SIZE = 16 * 1024;
protected final UsbDevice mDevice;
protected final Object mReadBufferLock = new Object();
protected final Object mWriteBufferLock = new Object();
protected UsbDeviceConnection mConnection = null;
private int _permissionStatus = permissionStatusRequestRequired;
/** Internal read buffer. Guarded by {@link #mReadBufferLock}. */
protected byte[] mReadBuffer;
/** Internal write buffer. Guarded by {@link #mWriteBufferLock}. */
protected byte[] mWriteBuffer;
public CommonUsbSerialDriver(UsbDevice device) {
mDevice = device;
mReadBuffer = new byte[DEFAULT_READ_BUFFER_SIZE];
mWriteBuffer = new byte[DEFAULT_WRITE_BUFFER_SIZE];
}
@Override
public void setConnection(UsbDeviceConnection connection) {
mConnection = connection;
}
@Override
public int permissionStatus() {
return _permissionStatus;
}
@Override
public void setPermissionStatus(int permissionStatus) {
_permissionStatus = permissionStatus;
}
/**
* Returns the currently-bound USB device.
*
* @return the device
*/
@Override
public final UsbDevice getDevice() {
return mDevice;
}
/**
* Returns the currently-bound USB device connection.
*
* @return the device connection
*/
@Override
public final UsbDeviceConnection getDeviceConnection() {
return mConnection;
}
/**
* Sets the size of the internal buffer used to exchange data with the USB
* stack for read operations. Most users should not need to change this.
*
* @param bufferSize the size in bytes
*/
public final void setReadBufferSize(int bufferSize) {
synchronized (mReadBufferLock) {
if (bufferSize == mReadBuffer.length) {
return;
}
mReadBuffer = new byte[bufferSize];
}
}
/**
* Sets the size of the internal buffer used to exchange data with the USB
* stack for write operations. Most users should not need to change this.
*
* @param bufferSize the size in bytes
*/
public final void setWriteBufferSize(int bufferSize) {
synchronized (mWriteBufferLock) {
if (bufferSize == mWriteBuffer.length) {
return;
}
mWriteBuffer = new byte[bufferSize];
}
}
@Override
public abstract void open() throws IOException;
@Override
public abstract void close() throws IOException;
@Override
public abstract int read(final byte[] dest, final int timeoutMillis) throws IOException;
@Override
public abstract int write(final byte[] src, final int timeoutMillis) throws IOException;
@Override
public abstract void setParameters(
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
@Override
public abstract boolean getCD() throws IOException;
@Override
public abstract boolean getCTS() throws IOException;
@Override
public abstract boolean getDSR() throws IOException;
@Override
public abstract boolean getDTR() throws IOException;
@Override
public abstract void setDTR(boolean value) throws IOException;
@Override
public abstract boolean getRI() throws IOException;
@Override
public abstract boolean getRTS() throws IOException;
@Override
public abstract void setRTS(boolean value) throws IOException;
@Override
public boolean purgeHwBuffers(boolean flushReadBuffers, boolean flushWriteBuffers) throws IOException {
return !flushReadBuffers && !flushWriteBuffers;
}
}

View File

@ -0,0 +1,292 @@
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
public class Cp2102SerialDriver extends CommonUsbSerialDriver {
private static final String TAG = Cp2102SerialDriver.class.getSimpleName();
private static final int DEFAULT_BAUD_RATE = 9600;
private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
/*
* Configuration Request Types
*/
private static final int REQTYPE_HOST_TO_DEVICE = 0x41;
/*
* Configuration Request Codes
*/
private static final int SILABSER_IFC_ENABLE_REQUEST_CODE = 0x00;
private static final int SILABSER_SET_BAUDDIV_REQUEST_CODE = 0x01;
private static final int SILABSER_SET_LINE_CTL_REQUEST_CODE = 0x03;
private static final int SILABSER_SET_MHS_REQUEST_CODE = 0x07;
private static final int SILABSER_SET_BAUDRATE = 0x1E;
private static final int SILABSER_FLUSH_REQUEST_CODE = 0x12;
private static final int FLUSH_READ_CODE = 0x0a;
private static final int FLUSH_WRITE_CODE = 0x05;
/*
* SILABSER_IFC_ENABLE_REQUEST_CODE
*/
private static final int UART_ENABLE = 0x0001;
private static final int UART_DISABLE = 0x0000;
/*
* SILABSER_SET_BAUDDIV_REQUEST_CODE
*/
private static final int BAUD_RATE_GEN_FREQ = 0x384000;
/*
* SILABSER_SET_MHS_REQUEST_CODE
*/
private static final int MCR_DTR = 0x0001;
private static final int MCR_RTS = 0x0002;
private static final int MCR_ALL = 0x0003;
private static final int CONTROL_WRITE_DTR = 0x0100;
private static final int CONTROL_WRITE_RTS = 0x0200;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
public Cp2102SerialDriver(UsbDevice device) {
super(device);
}
private int setConfigSingle(int request, int value) {
return mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, request, value,
0, null, 0, USB_WRITE_TIMEOUT_MILLIS);
}
@Override
public void open() throws IOException {
boolean opened = false;
try {
for (int i = 0; i < mDevice.getInterfaceCount(); i++) {
UsbInterface usbIface = mDevice.getInterface(i);
if (mConnection.claimInterface(usbIface, true)) {
Log.d(TAG, "claimInterface " + i + " SUCCESS");
} else {
Log.d(TAG, "claimInterface " + i + " FAIL");
}
}
UsbInterface dataIface = mDevice.getInterface(mDevice.getInterfaceCount() - 1);
for (int i = 0; i < dataIface.getEndpointCount(); i++) {
UsbEndpoint ep = dataIface.getEndpoint(i);
if (ep.getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (ep.getDirection() == UsbConstants.USB_DIR_IN) {
mReadEndpoint = ep;
} else {
mWriteEndpoint = ep;
}
}
}
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_ENABLE);
setConfigSingle(SILABSER_SET_MHS_REQUEST_CODE, MCR_ALL | CONTROL_WRITE_DTR | CONTROL_WRITE_RTS);
setConfigSingle(SILABSER_SET_BAUDDIV_REQUEST_CODE, BAUD_RATE_GEN_FREQ / DEFAULT_BAUD_RATE);
// setParameters(DEFAULT_BAUD_RATE, DEFAULT_DATA_BITS, DEFAULT_STOP_BITS, DEFAULT_PARITY);
opened = true;
} finally {
if (!opened) {
close();
}
}
}
@Override
public void close() throws IOException {
setConfigSingle(SILABSER_IFC_ENABLE_REQUEST_CODE, UART_DISABLE);
mConnection.close();
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
final int numBytesRead;
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer, readAmt,
timeoutMillis);
if (numBytesRead < 0) {
// This sucks: we get -1 on timeout, not 0 as preferred.
// We *should* use UsbRequest, except it has a bug/api oversight
// where there is no way to determine the number of bytes read
// in response :\ -- http://b.android.com/28023
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
}
return numBytesRead;
}
@Override
public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0;
while (offset < src.length) {
final int writeLength;
final int amtWritten;
synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) {
writeBuffer = src;
} else {
// bulkTransfer does not support offsets, make a copy.
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint, writeBuffer, writeLength,
timeoutMillis);
}
if (amtWritten <= 0) {
throw new IOException("Error writing " + writeLength
+ " bytes at offset " + offset + " length=" + src.length);
}
//Log.d(TAG, "Wrote amt=" + amtWritten + " attempted=" + writeLength);
offset += amtWritten;
}
return offset;
}
private void setBaudRate(int baudRate) throws IOException {
byte[] data = new byte[] {
(byte) ( baudRate & 0xff),
(byte) ((baudRate >> 8 ) & 0xff),
(byte) ((baudRate >> 16) & 0xff),
(byte) ((baudRate >> 24) & 0xff)
};
int ret = mConnection.controlTransfer(REQTYPE_HOST_TO_DEVICE, SILABSER_SET_BAUDRATE,
0, 0, data, 4, USB_WRITE_TIMEOUT_MILLIS);
if (ret < 0) {
throw new IOException("Error setting baud rate.");
}
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity)
throws IOException {
setBaudRate(baudRate);
int configDataBits = 0;
switch (dataBits) {
case DATABITS_5:
configDataBits |= 0x0500;
break;
case DATABITS_6:
configDataBits |= 0x0600;
break;
case DATABITS_7:
configDataBits |= 0x0700;
break;
case DATABITS_8:
configDataBits |= 0x0800;
break;
default:
configDataBits |= 0x0800;
break;
}
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configDataBits);
int configParityBits = 0; // PARITY_NONE
switch (parity) {
case PARITY_ODD:
configParityBits |= 0x0010;
break;
case PARITY_EVEN:
configParityBits |= 0x0020;
break;
}
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configParityBits);
int configStopBits = 0;
switch (stopBits) {
case STOPBITS_1:
configStopBits |= 0;
break;
case STOPBITS_2:
configStopBits |= 2;
break;
}
setConfigSingle(SILABSER_SET_LINE_CTL_REQUEST_CODE, configStopBits);
}
@Override
public boolean getCD() throws IOException {
return false;
}
@Override
public boolean getCTS() throws IOException {
return false;
}
@Override
public boolean getDSR() throws IOException {
return false;
}
@Override
public boolean getDTR() throws IOException {
return true;
}
@Override
public void setDTR(boolean value) throws IOException {
}
@Override
public boolean getRI() throws IOException {
return false;
}
@Override
public boolean getRTS() throws IOException {
return true;
}
@Override
public boolean purgeHwBuffers(boolean purgeReadBuffers,
boolean purgeWriteBuffers) throws IOException {
int value = (purgeReadBuffers ? FLUSH_READ_CODE : 0)
| (purgeWriteBuffers ? FLUSH_WRITE_CODE : 0);
if (value != 0) {
setConfigSingle(SILABSER_FLUSH_REQUEST_CODE, value);
}
return true;
}
@Override
public void setRTS(boolean value) throws IOException {
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_SILAB),
new int[] {
UsbId.SILAB_CP2102
});
return supportedDevices;
}
}

View File

@ -0,0 +1,445 @@
/* Copyright 2011 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
// IMPORTANT NOTE:
// This code has been modified from the original source. It now uses the FTDI driver provided by
// ftdichip.com to communicate with an FTDI device. The previous code did not work with all FTDI
// devices.
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbRequest;
import android.util.Log;
import com.ftdi.j2xx.D2xxManager;
import com.ftdi.j2xx.FT_Device;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.LinkedHashMap;
import java.util.Map;
import org.mavlink.qgroundcontrol.QGCActivity;
/**
* A {@link CommonUsbSerialDriver} implementation for a variety of FTDI devices
* <p>
* This driver is based on
* <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>, and is
* copyright and subject to the following terms:
*
* <pre>
* Copyright (C) 2003 by Intra2net AG
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation;
*
* opensource@intra2net.com
* http://www.intra2net.com/en/developer/libftdi
* </pre>
*
* </p>
* <p>
* Some FTDI devices have not been tested; see later listing of supported and
* unsupported devices. Devices listed as "supported" support the following
* features:
* <ul>
* <li>Read and write of serial data (see {@link #read(byte[], int)} and
* {@link #write(byte[], int)}.
* <li>Setting baud rate (see {@link #setBaudRate(int)}).
* </ul>
* </p>
* <p>
* Supported and tested devices:
* <ul>
* <li>{@value DeviceType#TYPE_R}</li>
* </ul>
* </p>
* <p>
* Unsupported but possibly working devices (please contact the author with
* feedback or patches):
* <ul>
* <li>{@value DeviceType#TYPE_2232C}</li>
* <li>{@value DeviceType#TYPE_2232H}</li>
* <li>{@value DeviceType#TYPE_4232H}</li>
* <li>{@value DeviceType#TYPE_AM}</li>
* <li>{@value DeviceType#TYPE_BM}</li>
* </ul>
* </p>
*
* @author mike wakerly (opensource@hoho.com)
* @see <a href="http://code.google.com/p/usb-serial-for-android/">USB Serial
* for Android project page</a>
* @see <a href="http://www.ftdichip.com/">FTDI Homepage</a>
* @see <a href="http://www.intra2net.com/en/developer/libftdi">libftdi</a>
*/
public class FtdiSerialDriver extends CommonUsbSerialDriver {
public static final int USB_TYPE_STANDARD = 0x00 << 5;
public static final int USB_TYPE_CLASS = 0x00 << 5;
public static final int USB_TYPE_VENDOR = 0x00 << 5;
public static final int USB_TYPE_RESERVED = 0x00 << 5;
public static final int USB_RECIP_DEVICE = 0x00;
public static final int USB_RECIP_INTERFACE = 0x01;
public static final int USB_RECIP_ENDPOINT = 0x02;
public static final int USB_RECIP_OTHER = 0x03;
public static final int USB_ENDPOINT_IN = 0x80;
public static final int USB_ENDPOINT_OUT = 0x00;
public static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
public static final int USB_READ_TIMEOUT_MILLIS = 5000;
// From ftdi.h
/**
* Reset the port.
*/
private static final int SIO_RESET_REQUEST = 0;
/**
* Set the modem control register.
*/
private static final int SIO_MODEM_CTRL_REQUEST = 1;
/**
* Set flow control register.
*/
private static final int SIO_SET_FLOW_CTRL_REQUEST = 2;
/**
* Set baud rate.
*/
private static final int SIO_SET_BAUD_RATE_REQUEST = 3;
/**
* Set the data characteristics of the port.
*/
private static final int SIO_SET_DATA_REQUEST = 4;
private static final int SIO_RESET_SIO = 0;
private static final int SIO_RESET_PURGE_RX = 1;
private static final int SIO_RESET_PURGE_TX = 2;
public static final int FTDI_DEVICE_OUT_REQTYPE =
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_OUT;
public static final int FTDI_DEVICE_IN_REQTYPE =
UsbConstants.USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_ENDPOINT_IN;
/**
* Length of the modem status header, transmitted with every read.
*/
private static final int MODEM_STATUS_HEADER_LENGTH = 2;
private final String TAG = FtdiSerialDriver.class.getSimpleName();
private DeviceType mType;
/**
* FTDI chip types.
*/
private static enum DeviceType {
TYPE_BM, TYPE_AM, TYPE_2232C, TYPE_R, TYPE_2232H, TYPE_4232H;
}
private int mInterface = 0; /* INTERFACE_ANY */
private int mMaxPacketSize = 64; // TODO(mikey): detect
/**
* Due to http://b.android.com/28023 , we cannot use UsbRequest async reads
* since it gives no indication of number of bytes read. Set this to
* {@code true} on platforms where it is fixed.
*/
private static final boolean ENABLE_ASYNC_READS = false;
FT_Device m_ftDev;
/**
* Filter FTDI status bytes from buffer
* @param src The source buffer (which contains status bytes)
* @param dest The destination buffer to write the status bytes into (can be src)
* @param totalBytesRead Number of bytes read to src
* @param maxPacketSize The USB endpoint max packet size
* @return The number of payload bytes
*/
private final int filterStatusBytes(byte[] src, byte[] dest, int totalBytesRead, int maxPacketSize) {
final int packetsCount = totalBytesRead / maxPacketSize + 1;
for (int packetIdx = 0; packetIdx < packetsCount; ++packetIdx) {
final int count = (packetIdx == (packetsCount - 1))
? (totalBytesRead % maxPacketSize) - MODEM_STATUS_HEADER_LENGTH
: maxPacketSize - MODEM_STATUS_HEADER_LENGTH;
if (count > 0) {
System.arraycopy(src,
packetIdx * maxPacketSize + MODEM_STATUS_HEADER_LENGTH,
dest,
packetIdx * (maxPacketSize - MODEM_STATUS_HEADER_LENGTH),
count);
}
}
return totalBytesRead - (packetsCount * 2);
}
/**
* Constructor.
*
* @param usbDevice the {@link UsbDevice} to use
* @param usbConnection the {@link UsbDeviceConnection} to use
* @throws UsbSerialRuntimeException if the given device is incompatible
* with this driver
*/
public FtdiSerialDriver(UsbDevice usbDevice) {
super(usbDevice);
mType = null;
}
public void reset() throws IOException {
int result = mConnection.controlTransfer(FTDI_DEVICE_OUT_REQTYPE, SIO_RESET_REQUEST,
SIO_RESET_SIO, 0 /* index */, null, 0, USB_WRITE_TIMEOUT_MILLIS);
if (result != 0) {
throw new IOException("Reset failed: result=" + result);
}
// TODO(mikey): autodetect.
mType = DeviceType.TYPE_R;
}
@Override
public void open() throws IOException {
D2xxManager ftD2xx = null;
try {
ftD2xx = D2xxManager.getInstance(QGCActivity.m_context);
} catch (D2xxManager.D2xxException ex) {
QGCActivity.qgcLogDebug("D2xxManager.getInstance threw exception: " + ex.getMessage());
}
if (ftD2xx == null) {
String errMsg = "Unable to retrieve D2xxManager instance.";
QGCActivity.qgcLogWarning(errMsg);
throw new IOException(errMsg);
}
QGCActivity.qgcLogDebug("Opened D2xxManager");
int DevCount = ftD2xx.createDeviceInfoList(QGCActivity.m_context);
QGCActivity.qgcLogDebug("Found " + DevCount + " ftdi devices.");
if (DevCount < 1) {
throw new IOException("No FTDI Devices found");
}
m_ftDev = null;
try {
m_ftDev = ftD2xx.openByIndex(QGCActivity.m_context, 0);
} catch (NullPointerException e) {
QGCActivity.qgcLogDebug("ftD2xx.openByIndex exception: " + e.getMessage());
} finally {
if (m_ftDev == null) {
throw new IOException("No FTDI Devices found");
}
}
QGCActivity.qgcLogDebug("Opened FTDI device.");
}
@Override
public void close() {
if (m_ftDev != null) {
try {
m_ftDev.close();
} catch (Exception e) {
QGCActivity.qgcLogWarning("close exception: " + e.getMessage());
}
m_ftDev = null;
}
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
int totalBytesRead = 0;
int bytesAvailable = m_ftDev.getQueueStatus();
if (bytesAvailable > 0) {
bytesAvailable = Math.min(4096, bytesAvailable);
try {
totalBytesRead = m_ftDev.read(dest, bytesAvailable, timeoutMillis);
} catch (NullPointerException e) {
final String errorMsg = "Error reading: " + e.getMessage();
QGCActivity.qgcLogWarning(errorMsg);
throw new IOException(errorMsg, e);
}
}
return totalBytesRead;
}
@Override
public int write(byte[] src, int timeoutMillis) throws IOException {
try {
m_ftDev.write(src);
return src.length;
} catch (Exception e) {
QGCActivity.qgcLogWarning("Error writing: " + e.getMessage());
}
return 0;
}
private int setBaudRate(int baudRate) throws IOException {
try {
m_ftDev.setBaudRate(baudRate);
return baudRate;
} catch (Exception e) {
QGCActivity.qgcLogWarning("Error setting baud rate: " + e.getMessage());
}
return 0;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits, int parity) throws IOException {
setBaudRate(baudRate);
switch (dataBits) {
case 7:
dataBits = D2xxManager.FT_DATA_BITS_7;
break;
case 8:
default:
dataBits = D2xxManager.FT_DATA_BITS_8;
break;
}
switch (stopBits) {
default:
case 0:
stopBits = D2xxManager.FT_STOP_BITS_1;
break;
case 1:
stopBits = D2xxManager.FT_STOP_BITS_2;
break;
}
switch (parity) {
default:
case 0:
parity = D2xxManager.FT_PARITY_NONE;
break;
case 1:
parity = D2xxManager.FT_PARITY_ODD;
break;
case 2:
parity = D2xxManager.FT_PARITY_EVEN;
break;
case 3:
parity = D2xxManager.FT_PARITY_MARK;
break;
case 4:
parity = D2xxManager.FT_PARITY_SPACE;
break;
}
try {
m_ftDev.setDataCharacteristics((byte)dataBits, (byte)stopBits, (byte)parity);
} catch (Exception e) {
QGCActivity.qgcLogWarning("Error setDataCharacteristics: " + e.getMessage());
}
}
@Override
public boolean getCD() throws IOException {
return false;
}
@Override
public boolean getCTS() throws IOException {
return false;
}
@Override
public boolean getDSR() throws IOException {
return false;
}
@Override
public boolean getDTR() throws IOException {
return false;
}
@Override
public void setDTR(boolean value) throws IOException {
}
@Override
public boolean getRI() throws IOException {
return false;
}
@Override
public boolean getRTS() throws IOException {
return false;
}
@Override
public void setRTS(boolean value) throws IOException {
}
@Override
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
if (purgeReadBuffers) {
try {
m_ftDev.purge(D2xxManager.FT_PURGE_RX);
} catch (Exception e) {
String errMsg = "Error purgeHwBuffers(RX): "+ e.getMessage();
QGCActivity.qgcLogWarning(errMsg);
throw new IOException(errMsg);
}
}
if (purgeWriteBuffers) {
try {
m_ftDev.purge(D2xxManager.FT_PURGE_TX);
} catch (Exception e) {
String errMsg = "Error purgeHwBuffers(TX): " + e.getMessage();
QGCActivity.qgcLogWarning(errMsg);
throw new IOException(errMsg);
}
}
return true;
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_FTDI),
new int[] {
UsbId.FTDI_FT232R,
UsbId.FTDI_FT231X,
});
/*
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PX4),
new int[] {
UsbId.DEVICE_PX4FMU
});
*/
return supportedDevices;
}
}

View File

@ -0,0 +1,523 @@
/* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
/*
* Ported to usb-serial-for-android
* by Felix Hädicke <felixhaedicke@web.de>
*
* Based on the pyprolific driver written
* by Emmanuel Blot <emmanuel.blot@free.fr>
* See https://github.com/eblot/pyftdi
*/
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.util.Log;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.LinkedHashMap;
import java.util.Map;
public class ProlificSerialDriver extends CommonUsbSerialDriver {
private static final int USB_READ_TIMEOUT_MILLIS = 1000;
private static final int USB_WRITE_TIMEOUT_MILLIS = 5000;
private static final int USB_RECIP_INTERFACE = 0x01;
private static final int PROLIFIC_VENDOR_READ_REQUEST = 0x01;
private static final int PROLIFIC_VENDOR_WRITE_REQUEST = 0x01;
private static final int PROLIFIC_VENDOR_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
| UsbConstants.USB_TYPE_VENDOR;
private static final int PROLIFIC_VENDOR_IN_REQTYPE = UsbConstants.USB_DIR_IN
| UsbConstants.USB_TYPE_VENDOR;
private static final int PROLIFIC_CTRL_OUT_REQTYPE = UsbConstants.USB_DIR_OUT
| UsbConstants.USB_TYPE_CLASS | USB_RECIP_INTERFACE;
private static final int WRITE_ENDPOINT = 0x02;
private static final int READ_ENDPOINT = 0x83;
private static final int INTERRUPT_ENDPOINT = 0x81;
private static final int FLUSH_RX_REQUEST = 0x08;
private static final int FLUSH_TX_REQUEST = 0x09;
private static final int SET_LINE_REQUEST = 0x20;
private static final int SET_CONTROL_REQUEST = 0x22;
private static final int CONTROL_DTR = 0x01;
private static final int CONTROL_RTS = 0x02;
private static final int STATUS_FLAG_CD = 0x01;
private static final int STATUS_FLAG_DSR = 0x02;
private static final int STATUS_FLAG_RI = 0x08;
private static final int STATUS_FLAG_CTS = 0x80;
private static final int STATUS_BUFFER_SIZE = 10;
private static final int STATUS_BYTE_IDX = 8;
private static final int DEVICE_TYPE_HX = 0;
private static final int DEVICE_TYPE_0 = 1;
private static final int DEVICE_TYPE_1 = 2;
private int mDeviceType = DEVICE_TYPE_HX;
private UsbEndpoint mReadEndpoint;
private UsbEndpoint mWriteEndpoint;
private UsbEndpoint mInterruptEndpoint;
private int mControlLinesValue = 0;
private int mBaudRate = -1, mDataBits = -1, mStopBits = -1, mParity = -1;
private int mStatus = 0;
private volatile Thread mReadStatusThread = null;
private final Object mReadStatusThreadLock = new Object();
boolean mStopReadStatusThread = false;
private IOException mReadStatusException = null;
private final String TAG = ProlificSerialDriver.class.getSimpleName();
private final byte[] inControlTransfer(int requestType, int request,
int value, int index, int length) throws IOException {
byte[] buffer = new byte[length];
int result = mConnection.controlTransfer(requestType, request, value,
index, buffer, length, USB_READ_TIMEOUT_MILLIS);
if (result != length) {
throw new IOException(
String.format("ControlTransfer with value 0x%x failed: %d",
value, result));
}
return buffer;
}
private final void outControlTransfer(int requestType, int request,
int value, int index, byte[] data) throws IOException {
int length = (data == null) ? 0 : data.length;
int result = mConnection.controlTransfer(requestType, request, value,
index, data, length, USB_WRITE_TIMEOUT_MILLIS);
if (result != length) {
throw new IOException(
String.format("ControlTransfer with value 0x%x failed: %d",
value, result));
}
}
private final byte[] vendorIn(int value, int index, int length)
throws IOException {
return inControlTransfer(PROLIFIC_VENDOR_IN_REQTYPE,
PROLIFIC_VENDOR_READ_REQUEST, value, index, length);
}
private final void vendorOut(int value, int index, byte[] data)
throws IOException {
outControlTransfer(PROLIFIC_VENDOR_OUT_REQTYPE,
PROLIFIC_VENDOR_WRITE_REQUEST, value, index, data);
}
private final void ctrlOut(int request, int value, int index, byte[] data)
throws IOException {
outControlTransfer(PROLIFIC_CTRL_OUT_REQTYPE, request, value, index,
data);
}
private void doBlackMagic() throws IOException {
vendorIn(0x8484, 0, 1);
vendorOut(0x0404, 0, null);
vendorIn(0x8484, 0, 1);
vendorIn(0x8383, 0, 1);
vendorIn(0x8484, 0, 1);
vendorOut(0x0404, 1, null);
vendorIn(0x8484, 0, 1);
vendorIn(0x8383, 0, 1);
vendorOut(0, 1, null);
vendorOut(1, 0, null);
vendorOut(2, (mDeviceType == DEVICE_TYPE_HX) ? 0x44 : 0x24, null);
}
private void resetDevice() throws IOException {
purgeHwBuffers(true, true);
}
private void setControlLines(int newControlLinesValue) throws IOException {
ctrlOut(SET_CONTROL_REQUEST, newControlLinesValue, 0, null);
mControlLinesValue = newControlLinesValue;
}
private final void readStatusThreadFunction() {
try {
while (!mStopReadStatusThread) {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
int readBytesCount = mConnection.bulkTransfer(mInterruptEndpoint,
buffer,
STATUS_BUFFER_SIZE,
500);
if (readBytesCount > 0) {
if (readBytesCount == STATUS_BUFFER_SIZE) {
mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
} else {
throw new IOException(
String.format("Invalid CTS / DSR / CD / RI status buffer received, expected %d bytes, but received %d",
STATUS_BUFFER_SIZE,
readBytesCount));
}
}
}
} catch (IOException e) {
mReadStatusException = e;
}
}
private final int getStatus() throws IOException {
if ((mReadStatusThread == null) && (mReadStatusException == null)) {
synchronized (mReadStatusThreadLock) {
if (mReadStatusThread == null) {
byte[] buffer = new byte[STATUS_BUFFER_SIZE];
int readBytes = mConnection.bulkTransfer(mInterruptEndpoint,
buffer,
STATUS_BUFFER_SIZE,
100);
if (readBytes != STATUS_BUFFER_SIZE) {
Log.w(TAG, "Could not read initial CTS / DSR / CD / RI status");
} else {
mStatus = buffer[STATUS_BYTE_IDX] & 0xff;
}
mReadStatusThread = new Thread(new Runnable() {
@Override
public void run() {
readStatusThreadFunction();
}
});
mReadStatusThread.setDaemon(true);
mReadStatusThread.start();
}
}
}
/* throw and clear an exception which occurred in the status read thread */
IOException readStatusException = mReadStatusException;
if (mReadStatusException != null) {
mReadStatusException = null;
throw readStatusException;
}
return mStatus;
}
private final boolean testStatusFlag(int flag) throws IOException {
return ((getStatus() & flag) == flag);
}
public ProlificSerialDriver(UsbDevice device) {
super(device);
}
@Override
public void open() throws IOException {
UsbInterface usbInterface = mDevice.getInterface(0);
if (!mConnection.claimInterface(usbInterface, true)) {
throw new IOException("Error claiming Prolific interface 0");
}
boolean openSuccessful = false;
try {
for (int i = 0; i < usbInterface.getEndpointCount(); ++i) {
UsbEndpoint currentEndpoint = usbInterface.getEndpoint(i);
switch (currentEndpoint.getAddress()) {
case READ_ENDPOINT:
mReadEndpoint = currentEndpoint;
break;
case WRITE_ENDPOINT:
mWriteEndpoint = currentEndpoint;
break;
case INTERRUPT_ENDPOINT:
mInterruptEndpoint = currentEndpoint;
break;
}
}
if (mDevice.getDeviceClass() == 0x02) {
mDeviceType = DEVICE_TYPE_0;
} else {
try {
Method getRawDescriptorsMethod
= mConnection.getClass().getMethod("getRawDescriptors");
byte[] rawDescriptors
= (byte[]) getRawDescriptorsMethod.invoke(mConnection);
byte maxPacketSize0 = rawDescriptors[7];
if (maxPacketSize0 == 64) {
mDeviceType = DEVICE_TYPE_HX;
} else if ((mDevice.getDeviceClass() == 0x00)
|| (mDevice.getDeviceClass() == 0xff)) {
mDeviceType = DEVICE_TYPE_1;
} else {
Log.w(TAG, "Could not detect PL2303 subtype, "
+ "Assuming that it is a HX device");
mDeviceType = DEVICE_TYPE_HX;
}
} catch (NoSuchMethodException e) {
Log.w(TAG, "Method UsbDeviceConnection.getRawDescriptors, "
+ "required for PL2303 subtype detection, not "
+ "available! Assuming that it is a HX device");
mDeviceType = DEVICE_TYPE_HX;
} catch (Exception e) {
Log.e(TAG, "An unexpected exception occurred while trying "
+ "to detect PL2303 subtype", e);
}
}
setControlLines(mControlLinesValue);
resetDevice();
doBlackMagic();
openSuccessful = true;
} finally {
if (!openSuccessful) {
try {
mConnection.releaseInterface(usbInterface);
} catch (Exception ingored) {
// Do not cover possible exceptions
}
}
}
}
@Override
public void close() throws IOException {
try {
mStopReadStatusThread = true;
synchronized (mReadStatusThreadLock) {
if (mReadStatusThread != null) {
try {
mReadStatusThread.join();
} catch (Exception e) {
Log.w(TAG, "An error occurred while waiting for status read thread", e);
}
}
}
resetDevice();
} finally {
mConnection.releaseInterface(mDevice.getInterface(0));
}
}
@Override
public int read(byte[] dest, int timeoutMillis) throws IOException {
synchronized (mReadBufferLock) {
int readAmt = Math.min(dest.length, mReadBuffer.length);
int numBytesRead = mConnection.bulkTransfer(mReadEndpoint, mReadBuffer,
readAmt, timeoutMillis);
if (numBytesRead < 0) {
return 0;
}
System.arraycopy(mReadBuffer, 0, dest, 0, numBytesRead);
return numBytesRead;
}
}
@Override
public int write(byte[] src, int timeoutMillis) throws IOException {
int offset = 0;
while (offset < src.length) {
final int writeLength;
final int amtWritten;
synchronized (mWriteBufferLock) {
final byte[] writeBuffer;
writeLength = Math.min(src.length - offset, mWriteBuffer.length);
if (offset == 0) {
writeBuffer = src;
} else {
// bulkTransfer does not support offsets, make a copy.
System.arraycopy(src, offset, mWriteBuffer, 0, writeLength);
writeBuffer = mWriteBuffer;
}
amtWritten = mConnection.bulkTransfer(mWriteEndpoint,
writeBuffer, writeLength, timeoutMillis);
}
if (amtWritten <= 0) {
throw new IOException("Error writing " + writeLength
+ " bytes at offset " + offset + " length="
+ src.length);
}
offset += amtWritten;
}
return offset;
}
@Override
public void setParameters(int baudRate, int dataBits, int stopBits,
int parity) throws IOException {
if ((mBaudRate == baudRate) && (mDataBits == dataBits)
&& (mStopBits == stopBits) && (mParity == parity)) {
// Make sure no action is performed if there is nothing to change
return;
}
byte[] lineRequestData = new byte[7];
lineRequestData[0] = (byte) (baudRate & 0xff);
lineRequestData[1] = (byte) ((baudRate >> 8) & 0xff);
lineRequestData[2] = (byte) ((baudRate >> 16) & 0xff);
lineRequestData[3] = (byte) ((baudRate >> 24) & 0xff);
switch (stopBits) {
case STOPBITS_1:
lineRequestData[4] = 0;
break;
case STOPBITS_1_5:
lineRequestData[4] = 1;
break;
case STOPBITS_2:
lineRequestData[4] = 2;
break;
default:
throw new IllegalArgumentException("Unknown stopBits value: " + stopBits);
}
switch (parity) {
case PARITY_NONE:
lineRequestData[5] = 0;
break;
case PARITY_ODD:
lineRequestData[5] = 1;
break;
case PARITY_EVEN:
lineRequestData[5] = 2;
break;
case PARITY_MARK:
lineRequestData[5] = 3;
break;
case PARITY_SPACE:
lineRequestData[5] = 4;
break;
default:
throw new IllegalArgumentException("Unknown parity value: " + parity);
}
lineRequestData[6] = (byte) dataBits;
ctrlOut(SET_LINE_REQUEST, 0, 0, lineRequestData);
resetDevice();
mBaudRate = baudRate;
mDataBits = dataBits;
mStopBits = stopBits;
mParity = parity;
}
@Override
public boolean getCD() throws IOException {
return testStatusFlag(STATUS_FLAG_CD);
}
@Override
public boolean getCTS() throws IOException {
return testStatusFlag(STATUS_FLAG_CTS);
}
@Override
public boolean getDSR() throws IOException {
return testStatusFlag(STATUS_FLAG_DSR);
}
@Override
public boolean getDTR() throws IOException {
return ((mControlLinesValue & CONTROL_DTR) == CONTROL_DTR);
}
@Override
public void setDTR(boolean value) throws IOException {
int newControlLinesValue;
if (value) {
newControlLinesValue = mControlLinesValue | CONTROL_DTR;
} else {
newControlLinesValue = mControlLinesValue & ~CONTROL_DTR;
}
setControlLines(newControlLinesValue);
}
@Override
public boolean getRI() throws IOException {
return testStatusFlag(STATUS_FLAG_RI);
}
@Override
public boolean getRTS() throws IOException {
return ((mControlLinesValue & CONTROL_RTS) == CONTROL_RTS);
}
@Override
public void setRTS(boolean value) throws IOException {
int newControlLinesValue;
if (value) {
newControlLinesValue = mControlLinesValue | CONTROL_RTS;
} else {
newControlLinesValue = mControlLinesValue & ~CONTROL_RTS;
}
setControlLines(newControlLinesValue);
}
@Override
public boolean purgeHwBuffers(boolean purgeReadBuffers, boolean purgeWriteBuffers) throws IOException {
if (purgeReadBuffers) {
vendorOut(FLUSH_RX_REQUEST, 0, null);
}
if (purgeWriteBuffers) {
vendorOut(FLUSH_TX_REQUEST, 0, null);
}
return true;
}
public static Map<Integer, int[]> getSupportedDevices() {
final Map<Integer, int[]> supportedDevices = new LinkedHashMap<Integer, int[]>();
supportedDevices.put(Integer.valueOf(UsbId.VENDOR_PROLIFIC),
new int[] { UsbId.PROLIFIC_PL2303, });
return supportedDevices;
}
}

View File

@ -0,0 +1,88 @@
/* Copyright 2012 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
package com.hoho.android.usbserial.driver;
/**
* Registry of USB vendor/product ID constants.
*
* Culled from various sources; see
* <a href="http://www.linux-usb.org/usb.ids">usb.ids</a> for one listing.
*
* @author mike wakerly (opensource@hoho.com)
*/
public final class UsbId {
public static final int VENDOR_FTDI = 0x0403;
public static final int FTDI_FT232R = 0x6001;
public static final int FTDI_FT231X = 0x6015;
public static final int VENDOR_PX4 = 0x26AC;
public static final int DEVICE_PX4FMU = 0x11;
public static final int VENDOR_ATMEL = 0x03EB;
public static final int ATMEL_LUFA_CDC_DEMO_APP = 0x2044;
public static final int VENDOR_ARDUINO = 0x2341;
public static final int ARDUINO_UNO = 0x0001;
public static final int ARDUINO_MEGA_2560 = 0x0010;
public static final int ARDUINO_SERIAL_ADAPTER = 0x003b;
public static final int ARDUINO_MEGA_ADK = 0x003f;
public static final int ARDUINO_MEGA_2560_R3 = 0x0042;
public static final int ARDUINO_UNO_R3 = 0x0043;
public static final int ARDUINO_MEGA_ADK_R3 = 0x0044;
public static final int ARDUINO_SERIAL_ADAPTER_R3 = 0x0044;
public static final int ARDUINO_LEONARDO = 0x8036;
public static final int VENDOR_VAN_OOIJEN_TECH = 0x16c0;
public static final int VAN_OOIJEN_TECH_TEENSYDUINO_SERIAL = 0x0483;
public static final int VENDOR_LEAFLABS = 0x1eaf;
public static final int LEAFLABS_MAPLE = 0x0004;
public static final int VENDOR_SILAB = 0x10c4;
public static final int SILAB_CP2102 = 0xea60;
public static final int VENDOR_PROLIFIC = 0x067b;
public static final int PROLIFIC_PL2303 = 0x2303;
public static final int VENDOR_UBLOX = 0x1546;
public static final int DEVICE_UBLOX_5 = 0x01a5;
public static final int DEVICE_UBLOX_6 = 0x01a6;
public static final int DEVICE_UBLOX_7 = 0x01a7;
public static final int DEVICE_UBLOX_8 = 0x01a8;
public static final int VENDOR_OPENPILOT = 0x20A0;
public static final int DEVICE_REVOLUTION = 0x415E;
public static final int DEVICE_OPLINK = 0x415C;
public static final int DEVICE_SPARKY2 = 0x41D0;
public static final int DEVICE_CC3D = 0x415D;
public static final int VENDOR_ARDUPILOT_CHIBIOS1 = 0x0483;
public static final int VENDOR_ARDUPILOT_CHIBIOS2 = 0x1209;
public static final int DEVICE_ARDUPILOT_CHIBIOS = 0x5740;
public static final int VENDOR_DRAGONLINK = 0x1FC9;
public static final int DEVICE_DRAGONLINK = 0x0083;
private UsbId() {
throw new IllegalAccessError("Non-instantiable class.");
}
}

View File

@ -0,0 +1,238 @@
/* Copyright 2011 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
package com.hoho.android.usbserial.driver;
import java.io.IOException;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
/**
* Driver interface for a USB serial device.
*
* @author mike wakerly (opensource@hoho.com)
*/
public interface UsbSerialDriver {
/** 5 data bits. */
public static final int DATABITS_5 = 5;
/** 6 data bits. */
public static final int DATABITS_6 = 6;
/** 7 data bits. */
public static final int DATABITS_7 = 7;
/** 8 data bits. */
public static final int DATABITS_8 = 8;
/** No flow control. */
public static final int FLOWCONTROL_NONE = 0;
/** RTS/CTS input flow control. */
public static final int FLOWCONTROL_RTSCTS_IN = 1;
/** RTS/CTS output flow control. */
public static final int FLOWCONTROL_RTSCTS_OUT = 2;
/** XON/XOFF input flow control. */
public static final int FLOWCONTROL_XONXOFF_IN = 4;
/** XON/XOFF output flow control. */
public static final int FLOWCONTROL_XONXOFF_OUT = 8;
/** No parity. */
public static final int PARITY_NONE = 0;
/** Odd parity. */
public static final int PARITY_ODD = 1;
/** Even parity. */
public static final int PARITY_EVEN = 2;
/** Mark parity. */
public static final int PARITY_MARK = 3;
/** Space parity. */
public static final int PARITY_SPACE = 4;
/** 1 stop bit. */
public static final int STOPBITS_1 = 1;
/** 1.5 stop bits. */
public static final int STOPBITS_1_5 = 3;
/** 2 stop bits. */
public static final int STOPBITS_2 = 2;
public static final int permissionStatusSuccess = 0;
public static final int permissionStatusDenied = 1;
public static final int permissionStatusRequested = 2;
public static final int permissionStatusRequestRequired = 3;
public static final int permissionStatusOpen = 4;
public int permissionStatus();
public void setPermissionStatus(int permissionStatus);
public void setConnection(UsbDeviceConnection connection);
/**
* Returns the currently-bound USB device.
*
* @return the device
*/
public UsbDevice getDevice();
/**
* Returns the currently-bound USB device.
*
* @return the device
*/
public UsbDeviceConnection getDeviceConnection();
/**
* Opens and initializes the device as a USB serial device. Upon success,
* caller must ensure that {@link #close()} is eventually called.
*
* @throws IOException on error opening or initializing the device.
*/
public void open() throws IOException;
/**
* Closes the serial device.
*
* @throws IOException on error closing the device.
*/
public void close() throws IOException;
/**
* Reads as many bytes as possible into the destination buffer.
*
* @param dest the destination byte buffer
* @param timeoutMillis the timeout for reading
* @return the actual number of bytes read
* @throws IOException if an error occurred during reading
*/
public int read(final byte[] dest, final int timeoutMillis) throws IOException;
/**
* Writes as many bytes as possible from the source buffer.
*
* @param src the source byte buffer
* @param timeoutMillis the timeout for writing
* @return the actual number of bytes written
* @throws IOException if an error occurred during writing
*/
public int write(final byte[] src, final int timeoutMillis) throws IOException;
/**
* Sets various serial port parameters.
*
* @param baudRate baud rate as an integer, for example {@code 115200}.
* @param dataBits one of {@link #DATABITS_5}, {@link #DATABITS_6},
* {@link #DATABITS_7}, or {@link #DATABITS_8}.
* @param stopBits one of {@link #STOPBITS_1}, {@link #STOPBITS_1_5}, or
* {@link #STOPBITS_2}.
* @param parity one of {@link #PARITY_NONE}, {@link #PARITY_ODD},
* {@link #PARITY_EVEN}, {@link #PARITY_MARK}, or
* {@link #PARITY_SPACE}.
* @throws IOException on error setting the port parameters
*/
public void setParameters(
int baudRate, int dataBits, int stopBits, int parity) throws IOException;
/**
* Gets the CD (Carrier Detect) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getCD() throws IOException;
/**
* Gets the CTS (Clear To Send) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getCTS() throws IOException;
/**
* Gets the DSR (Data Set Ready) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getDSR() throws IOException;
/**
* Gets the DTR (Data Terminal Ready) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getDTR() throws IOException;
/**
* Sets the DTR (Data Terminal Ready) bit on the underlying UART, if
* supported.
*
* @param value the value to set
* @throws IOException if an error occurred during writing
*/
public void setDTR(boolean value) throws IOException;
/**
* Gets the RI (Ring Indicator) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getRI() throws IOException;
/**
* Gets the RTS (Request To Send) bit from the underlying UART.
*
* @return the current state, or {@code false} if not supported.
* @throws IOException if an error occurred during reading
*/
public boolean getRTS() throws IOException;
/**
* Sets the RTS (Request To Send) bit on the underlying UART, if
* supported.
*
* @param value the value to set
* @throws IOException if an error occurred during writing
*/
public void setRTS(boolean value) throws IOException;
/**
* Flush non-transmitted output data and / or non-read input data
* @param flushRX {@code true} to flush non-transmitted output data
* @param flushTX {@code true} to flush non-read input data
* @return {@code true} if the operation was successful, or
* {@code false} if the operation is not supported by the driver or device
* @throws IOException if an error occurred during flush
*/
public boolean purgeHwBuffers(boolean flushRX, boolean flushTX) throws IOException;
}

View File

@ -0,0 +1,224 @@
/* Copyright 2011 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
// IMPORTANT NOTE:
// This source has been modified from the original such that testIfSupported only tests for a vendor id
// match. If that matches it allows all product ids through. This provides for better match on unknown boards.
package com.hoho.android.usbserial.driver;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Helper class which finds compatible {@link UsbDevice}s and creates
* {@link UsbSerialDriver} instances.
*
* <p/>
* You don't need a Prober to use the rest of the library: it is perfectly
* acceptable to instantiate driver instances manually. The Prober simply
* provides convenience functions.
*
* <p/>
* For most drivers, the corresponding {@link #probe(UsbManager, UsbDevice)}
* method will either return an empty list (device unknown / unsupported) or a
* singleton list. However, multi-port drivers may return multiple instances.
*
* @author mike wakerly (opensource@hoho.com)
*/
public enum UsbSerialProber {
// TODO(mikey): Too much boilerplate.
/**
* Prober for {@link FtdiSerialDriver}.
*
* @see FtdiSerialDriver
*/
FTDI_SERIAL {
@Override
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
if (!testIfSupported(usbDevice, FtdiSerialDriver.getSupportedDevices())) {
return Collections.emptyList();
}
final UsbSerialDriver driver = new FtdiSerialDriver(usbDevice);
return Collections.singletonList(driver);
}
},
CDC_ACM_SERIAL {
@Override
public List<UsbSerialDriver> probe(UsbManager manager, UsbDevice usbDevice) {
if (!testIfSupported(usbDevice, CdcAcmSerialDriver.getSupportedDevices())) {
return Collections.emptyList();
}
final UsbSerialDriver driver = new CdcAcmSerialDriver(usbDevice);
return Collections.singletonList(driver);
}
},
SILAB_SERIAL {
@Override
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
if (!testIfSupported(usbDevice, Cp2102SerialDriver.getSupportedDevices())) {
return Collections.emptyList();
}
final UsbSerialDriver driver = new Cp2102SerialDriver(usbDevice);
return Collections.singletonList(driver);
}
},
PROLIFIC_SERIAL {
@Override
public List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice) {
if (!testIfSupported(usbDevice, ProlificSerialDriver.getSupportedDevices())) {
return Collections.emptyList();
}
final UsbSerialDriver driver = new ProlificSerialDriver(usbDevice);
return Collections.singletonList(driver);
}
};
/**
* Tests the supplied {@link UsbDevice} for compatibility with this enum
* member, returning one or more driver instances if compatible.
*
* @param manager the {@link UsbManager} to use
* @param usbDevice the raw {@link UsbDevice} to use
* @return zero or more {@link UsbSerialDriver}, depending on compatibility
* (never {@code null}).
*/
protected abstract List<UsbSerialDriver> probe(final UsbManager manager, final UsbDevice usbDevice);
/**
* Creates and returns a new {@link UsbSerialDriver} instance for the first
* compatible {@link UsbDevice} found on the bus. If none are found,
* returns {@code null}.
*
* <p/>
* The order of devices is undefined, therefore if there are multiple
* devices on the bus, the chosen device may not be predictable (clients
* should use {@link #findAllDevices(UsbManager)} instead).
*
* @param usbManager the {@link UsbManager} to use.
* @return the first available {@link UsbSerialDriver}, or {@code null} if
* none are available.
*/
public static UsbSerialDriver findFirstDevice(final UsbManager usbManager) {
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
for (final UsbSerialProber prober : values()) {
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
if (!probedDevices.isEmpty()) {
return probedDevices.get(0);
}
}
}
return null;
}
/**
* Creates a new {@link UsbSerialDriver} instance for all compatible
* {@link UsbDevice}s found on the bus. If no compatible devices are found,
* the list will be empty.
*
* @param usbManager
* @return
*/
public static List<UsbSerialDriver> findAllDevices(final UsbManager usbManager) {
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
//Log.i("QGC_UsbSerialProber", "Looking for USB devices");
// For each UsbDevice, call probe() for each prober.
for (final UsbDevice usbDevice : usbManager.getDeviceList().values()) {
//Log.i("QGC_UsbSerialProber", "Probing device: " + usbDevice.getDeviceName() + " mid: " + usbDevice.getVendorId() + " pid: " + usbDevice.getDeviceId());
result.addAll(probeSingleDevice(usbManager, usbDevice));
}
return result;
}
/**
* Special method for testing a specific device for driver support,
* returning any compatible driver(s).
*
* <p/>
* Clients should ordinarily use {@link #findAllDevices(UsbManager)}, which
* operates against the entire bus of devices. This method is useful when
* testing against only a single target is desired.
*
* @param usbManager the {@link UsbManager} to use.
* @param usbDevice the device to test against.
* @return a list containing zero or more {@link UsbSerialDriver} instances.
*/
public static List<UsbSerialDriver> probeSingleDevice(final UsbManager usbManager,
UsbDevice usbDevice)
{
final List<UsbSerialDriver> result = new ArrayList<UsbSerialDriver>();
for (final UsbSerialProber prober : values()) {
final List<UsbSerialDriver> probedDevices = prober.probe(usbManager, usbDevice);
result.addAll(probedDevices);
}
return result;
}
/**
* Deprecated; Use {@link #findFirstDevice(UsbManager)}.
*
* @param usbManager
* @return
*/
@Deprecated
public static UsbSerialDriver acquire(final UsbManager usbManager) {
return findFirstDevice(usbManager);
}
/**
* Deprecated; use {@link #probeSingleDevice(UsbManager, UsbDevice)}.
*
* @param usbManager
* @param usbDevice
* @return
*/
@Deprecated
public static UsbSerialDriver acquire(final UsbManager usbManager, final UsbDevice usbDevice) {
final List<UsbSerialDriver> probedDevices = probeSingleDevice(usbManager, usbDevice);
if (!probedDevices.isEmpty()) {
return probedDevices.get(0);
}
return null;
}
/**
* Returns {@code true} if the given device is found in the driver's
* vendor/product map.
*
* @param usbDevice the device to test
* @param supportedDevices map of vendor IDs to product ID(s)
* @return {@code true} if supported
*/
private static boolean testIfSupported(final UsbDevice usbDevice, final Map<Integer, int[]> supportedDevices) {
return supportedDevices.containsKey(usbDevice.getVendorId());
}
}

View File

@ -0,0 +1,46 @@
/*
* Copyright 2011 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*/
package com.hoho.android.usbserial.driver;
/**
* Generic unchecked exception for the usbserial package.
*
* @author mike wakerly (opensource@hoho.com)
*/
@SuppressWarnings("serial")
public class UsbSerialRuntimeException extends RuntimeException {
public UsbSerialRuntimeException() {
super();
}
public UsbSerialRuntimeException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
public UsbSerialRuntimeException(String detailMessage) {
super(detailMessage);
}
public UsbSerialRuntimeException(Throwable throwable) {
super(throwable);
}
}

View File

@ -0,0 +1,60 @@
/*
* Copyright (C) 2012, Collabora Ltd.
* Author: Youness Alaoui
*
* Copyright (C) 2015, Collabora Ltd.
* Author: Justin Kim <justin.kim@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.freedesktop.gstreamer.androidmedia;
import android.hardware.Camera;
public class GstAhcCallback implements Camera.PreviewCallback,
Camera.ErrorCallback,
Camera.AutoFocusCallback {
public long mUserData;
public long mCallback;
public static native void gst_ah_camera_on_preview_frame(byte[] data, Camera camera,
long callback, long user_data);
public static native void gst_ah_camera_on_error(int error, Camera camera,
long callback, long user_data);
public static native void gst_ah_camera_on_auto_focus(boolean success, Camera camera,
long callback, long user_data);
public GstAhcCallback(long callback, long user_data) {
mCallback = callback;
mUserData = user_data;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
gst_ah_camera_on_preview_frame(data, camera, mCallback, mUserData);
}
@Override
public void onError(int error, Camera camera) {
gst_ah_camera_on_error(error, camera, mCallback, mUserData);
}
@Override
public void onAutoFocus(boolean success, Camera camera) {
gst_ah_camera_on_auto_focus(success, camera, mCallback, mUserData);
}
}

View File

@ -0,0 +1,54 @@
/*
* Copyright (C) 2016 SurroundIO
* Author: Martin Kelly <martin@surround.io>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
package org.freedesktop.gstreamer.androidmedia;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
public class GstAhsCallback implements SensorEventListener {
public long mUserData;
public long mSensorCallback;
public long mAccuracyCallback;
public static native void gst_ah_sensor_on_sensor_changed(SensorEvent event,
long callback, long user_data);
public static native void gst_ah_sensor_on_accuracy_changed(Sensor sensor, int accuracy,
long callback, long user_data);
public GstAhsCallback(long sensor_callback,
long accuracy_callback, long user_data) {
mSensorCallback = sensor_callback;
mAccuracyCallback = accuracy_callback;
mUserData = user_data;
}
@Override
public void onSensorChanged(SensorEvent event) {
gst_ah_sensor_on_sensor_changed(event, mSensorCallback, mUserData);
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
gst_ah_sensor_on_accuracy_changed(sensor, accuracy,
mAccuracyCallback, mUserData);
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright (C) 2015, Collabora Ltd.
* Author: Matthieu Bouron <matthieu.bouron@collabora.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.freedesktop.gstreamer.androidmedia;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
public class GstAmcOnFrameAvailableListener implements OnFrameAvailableListener
{
private long context = 0;
public synchronized void onFrameAvailable (SurfaceTexture surfaceTexture) {
native_onFrameAvailable(context, surfaceTexture);
}
public synchronized long getContext () {
return context;
}
public synchronized void setContext (long c) {
context = c;
}
private native void native_onFrameAvailable (long context, SurfaceTexture surfaceTexture);
}

View File

@ -0,0 +1,798 @@
package org.mavlink.qgroundcontrol;
/* Copyright 2013 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
///////////////////////////////////////////////////////////////////////////////////////////
// Written by: Mike Goza April 2014
//
// These routines interface with the Android USB Host devices for serial port communication.
// The code uses the usb-serial-for-android software library. The QGCActivity class is the
// interface to the C++ routines through jni calls. Do not change the functions without also
// changing the corresponding calls in the C++ routines or you will break the interface.
//
////////////////////////////////////////////////////////////////////////////////////////////
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.Timer;
import java.util.TimerTask;
import java.io.IOException;
import java.lang.reflect.Method;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbManager;
import android.widget.Toast;
import android.util.Log;
import android.os.PowerManager;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.app.PendingIntent;
import android.view.WindowManager;
import android.os.Bundle;
import android.bluetooth.BluetoothDevice;
import android.os.storage.StorageManager;
import android.os.storage.StorageVolume;
import com.hoho.android.usbserial.driver.*;
import org.qtproject.qt5.android.bindings.QtActivity;
import org.qtproject.qt5.android.bindings.QtApplication;
public class QGCActivity extends QtActivity
{
public static int BAD_DEVICE_ID = 0;
private static QGCActivity _instance = null;
private static UsbManager _usbManager = null;
private static List<UsbSerialDriver> _drivers;
private static HashMap<Integer, UsbIoManager> m_ioManager;
private static HashMap<Integer, Long> _userDataHashByDeviceId;
private static final String TAG = "QGC_QGCActivity";
private static PowerManager.WakeLock _wakeLock;
private static final String ACTION_USB_PERMISSION = "org.mavlink.qgroundcontrol.action.USB_PERMISSION";
private static PendingIntent _usbPermissionIntent = null;
private TaiSync taiSync = null;
private Timer probeAccessoriesTimer = null;
private static WifiManager.MulticastLock _wifiMulticastLock;
public static Context m_context;
private final static ExecutorService m_Executor = Executors.newSingleThreadExecutor();
private final static UsbIoManager.Listener m_Listener =
new UsbIoManager.Listener()
{
@Override
public void onRunError(Exception eA, long userData)
{
Log.e(TAG, "onRunError Exception");
nativeDeviceException(userData, eA.getMessage());
}
@Override
public void onNewData(final byte[] dataA, long userData)
{
nativeDeviceNewData(userData, dataA);
}
};
private final BroadcastReceiver mOpenAccessoryReceiver =
new BroadcastReceiver()
{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_USB_PERMISSION.equals(action)) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null && intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
openAccessory(accessory);
}
} else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
if (accessory != null) {
closeAccessory(accessory);
}
}
}
};
private static UsbSerialDriver _findDriverByDeviceId(int deviceId) {
for (UsbSerialDriver driver: _drivers) {
if (driver.getDevice().getDeviceId() == deviceId) {
return driver;
}
}
return null;
}
private static UsbSerialDriver _findDriverByDeviceName(String deviceName) {
for (UsbSerialDriver driver: _drivers) {
if (driver.getDevice().getDeviceName().equals(deviceName)) {
return driver;
}
}
return null;
}
private final static BroadcastReceiver _usbReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i(TAG, "BroadcastReceiver USB action " + action);
if (ACTION_USB_PERMISSION.equals(action)) {
synchronized (_instance) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
UsbSerialDriver driver = _findDriverByDeviceId(device.getDeviceId());
if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
qgcLogDebug("Permission granted to " + device.getDeviceName());
driver.setPermissionStatus(UsbSerialDriver.permissionStatusSuccess);
} else {
qgcLogDebug("Permission denied for " + device.getDeviceName());
driver.setPermissionStatus(UsbSerialDriver.permissionStatusDenied);
}
}
}
} else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
if (_userDataHashByDeviceId.containsKey(device.getDeviceId())) {
nativeDeviceHasDisconnected(_userDataHashByDeviceId.get(device.getDeviceId()));
}
}
}
try {
nativeUpdateAvailableJoysticks();
} catch(Exception e) {
Log.e(TAG, "Exception nativeUpdateAvailableJoysticks()");
}
}
};
// Native C++ functions which connect back to QSerialPort code
private static native void nativeDeviceHasDisconnected(long userData);
private static native void nativeDeviceException(long userData, String messageA);
private static native void nativeDeviceNewData(long userData, byte[] dataA);
private static native void nativeUpdateAvailableJoysticks();
// Native C++ functions called to log output
public static native void qgcLogDebug(String message);
public static native void qgcLogWarning(String message);
public native void nativeInit();
// QGCActivity singleton
public QGCActivity()
{
_instance = this;
_drivers = new ArrayList<UsbSerialDriver>();
_userDataHashByDeviceId = new HashMap<Integer, Long>();
m_ioManager = new HashMap<Integer, UsbIoManager>();
}
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
nativeInit();
PowerManager pm = (PowerManager)_instance.getSystemService(Context.POWER_SERVICE);
_wakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "QGroundControl");
if(_wakeLock != null) {
_wakeLock.acquire();
} else {
Log.i(TAG, "SCREEN_BRIGHT_WAKE_LOCK not acquired!!!");
}
_instance.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
_usbManager = (UsbManager)_instance.getSystemService(Context.USB_SERVICE);
// Register for USB Detach and USB Permission intent
IntentFilter filter = new IntentFilter();
filter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);
filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
filter.addAction(ACTION_USB_PERMISSION);
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
_instance.registerReceiver(_instance._usbReceiver, filter);
// Create intent for usb permission request
_usbPermissionIntent = PendingIntent.getBroadcast(_instance, 0, new Intent(ACTION_USB_PERMISSION), 0);
// Workaround for QTBUG-73138
if (_wifiMulticastLock == null)
{
WifiManager wifi = (WifiManager) _instance.getSystemService(Context.WIFI_SERVICE);
_wifiMulticastLock = wifi.createMulticastLock("QGroundControl");
_wifiMulticastLock.setReferenceCounted(true);
}
_wifiMulticastLock.acquire();
Log.d(TAG, "Multicast lock: " + _wifiMulticastLock.toString());
try {
taiSync = new TaiSync();
IntentFilter accessoryFilter = new IntentFilter(ACTION_USB_PERMISSION);
filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
registerReceiver(mOpenAccessoryReceiver, accessoryFilter);
probeAccessoriesTimer = new Timer();
probeAccessoriesTimer.schedule(new TimerTask() {
@Override
public void run()
{
probeAccessories();
}
}, 0, 3000);
} catch(Exception e) {
Log.e(TAG, "Exception: " + e);
}
}
@Override
public void onResume() {
super.onResume();
// Plug in of USB ACCESSORY triggers only onResume event.
// Then we scan if there is actually anything new
probeAccessories();
}
@Override
protected void onDestroy()
{
if (probeAccessoriesTimer != null) {
probeAccessoriesTimer.cancel();
}
unregisterReceiver(mOpenAccessoryReceiver);
try {
if (_wifiMulticastLock != null) {
_wifiMulticastLock.release();
Log.d(TAG, "Multicast lock released.");
}
if(_wakeLock != null) {
_wakeLock.release();
}
} catch(Exception e) {
Log.e(TAG, "Exception onDestroy()");
}
super.onDestroy();
}
public void onInit(int status) {
}
/// Incrementally updates the list of drivers connected to the device
private static void updateCurrentDrivers()
{
List<UsbSerialDriver> currentDrivers = UsbSerialProber.findAllDevices(_usbManager);
// Remove stale drivers
for (int i=_drivers.size()-1; i>=0; i--) {
boolean found = false;
for (UsbSerialDriver currentDriver: currentDrivers) {
if (_drivers.get(i).getDevice().getDeviceId() == currentDriver.getDevice().getDeviceId()) {
found = true;
break;
}
}
if (!found) {
qgcLogDebug("Remove stale driver " + _drivers.get(i).getDevice().getDeviceName());
_drivers.remove(i);
}
}
// Add new drivers
for (int i=0; i<currentDrivers.size(); i++) {
boolean found = false;
for (int j=0; j<_drivers.size(); j++) {
if (currentDrivers.get(i).getDevice().getDeviceId() == _drivers.get(j).getDevice().getDeviceId()) {
found = true;
break;
}
}
if (!found) {
UsbSerialDriver newDriver = currentDrivers.get(i);
UsbDevice device = newDriver.getDevice();
String deviceName = device.getDeviceName();
_drivers.add(newDriver);
qgcLogDebug("Adding new driver " + deviceName);
// Request permission if needed
if (_usbManager.hasPermission(device)) {
qgcLogDebug("Already have permission to use device " + deviceName);
newDriver.setPermissionStatus(UsbSerialDriver.permissionStatusSuccess);
} else {
qgcLogDebug("Requesting permission to use device " + deviceName);
newDriver.setPermissionStatus(UsbSerialDriver.permissionStatusRequested);
_usbManager.requestPermission(device, _usbPermissionIntent);
}
}
}
}
/// Returns array of device info for each unopened device.
/// @return Device info format DeviceName:Company:ProductId:VendorId
public static String[] availableDevicesInfo()
{
updateCurrentDrivers();
if (_drivers.size() <= 0) {
return null;
}
List<String> deviceInfoList = new ArrayList<String>();
for (int i=0; i<_drivers.size(); i++) {
String deviceInfo;
UsbSerialDriver driver = _drivers.get(i);
if (driver.permissionStatus() != UsbSerialDriver.permissionStatusSuccess) {
continue;
}
UsbDevice device = driver.getDevice();
deviceInfo = device.getDeviceName() + ":";
if (driver instanceof FtdiSerialDriver) {
deviceInfo = deviceInfo + "FTDI:";
} else if (driver instanceof CdcAcmSerialDriver) {
deviceInfo = deviceInfo + "Cdc Acm:";
} else if (driver instanceof Cp2102SerialDriver) {
deviceInfo = deviceInfo + "Cp2102:";
} else if (driver instanceof ProlificSerialDriver) {
deviceInfo = deviceInfo + "Prolific:";
} else {
deviceInfo = deviceInfo + "Unknown:";
}
deviceInfo = deviceInfo + Integer.toString(device.getProductId()) + ":";
deviceInfo = deviceInfo + Integer.toString(device.getVendorId()) + ":";
deviceInfoList.add(deviceInfo);
}
String[] rgDeviceInfo = new String[deviceInfoList.size()];
for (int i=0; i<deviceInfoList.size(); i++) {
rgDeviceInfo[i] = deviceInfoList.get(i);
}
return rgDeviceInfo;
}
/// Open the specified device
/// @param userData Data to associate with device and pass back through to native calls.
/// @return Device id
public static int open(Context parentContext, String deviceName, long userData)
{
int deviceId = BAD_DEVICE_ID;
m_context = parentContext;
UsbSerialDriver driver = _findDriverByDeviceName(deviceName);
if (driver == null) {
qgcLogWarning("Attempt to open unknown device " + deviceName);
return BAD_DEVICE_ID;
}
if (driver.permissionStatus() != UsbSerialDriver.permissionStatusSuccess) {
qgcLogWarning("Attempt to open device with incorrect permission status " + deviceName + " " + driver.permissionStatus());
return BAD_DEVICE_ID;
}
UsbDevice device = driver.getDevice();
deviceId = device.getDeviceId();
try {
driver.setConnection(_usbManager.openDevice(device));
driver.open();
driver.setPermissionStatus(UsbSerialDriver.permissionStatusOpen);
_userDataHashByDeviceId.put(deviceId, userData);
UsbIoManager ioManager = new UsbIoManager(driver, m_Listener, userData);
m_ioManager.put(deviceId, ioManager);
m_Executor.submit(ioManager);
qgcLogDebug("Port open successful");
} catch(IOException exA) {
driver.setPermissionStatus(UsbSerialDriver.permissionStatusRequestRequired);
_userDataHashByDeviceId.remove(deviceId);
if(m_ioManager.get(deviceId) != null) {
m_ioManager.get(deviceId).stop();
m_ioManager.remove(deviceId);
}
qgcLogWarning("Port open exception: " + exA.getMessage());
return BAD_DEVICE_ID;
}
return deviceId;
}
public static void startIoManager(int idA)
{
if (m_ioManager.get(idA) != null)
return;
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return;
UsbIoManager managerL = new UsbIoManager(driverL, m_Listener, _userDataHashByDeviceId.get(idA));
m_ioManager.put(idA, managerL);
m_Executor.submit(managerL);
}
public static void stopIoManager(int idA)
{
if(m_ioManager.get(idA) == null)
return;
m_ioManager.get(idA).stop();
m_ioManager.remove(idA);
}
///////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Sets the parameters on an open port.
//
// Args: idA - ID number from the open command
// baudRateA - Decimal value of the baud rate. I.E. 9600, 57600, 115200, etc.
// dataBitsA - number of data bits. Valid numbers are 5, 6, 7, 8
// stopBitsA - number of stop bits. Valid numbers are 1, 2
// parityA - No Parity=0, Odd Parity=1, Even Parity=2
//
// Returns: T/F Success/Failure
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
public static boolean setParameters(int idA, int baudRateA, int dataBitsA, int stopBitsA, int parityA)
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return false;
try
{
driverL.setParameters(baudRateA, dataBitsA, stopBitsA, parityA);
return true;
}
catch(IOException eA)
{
return false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Close the device.
//
// Args: idA - ID number from the open command
//
// Returns: T/F Success/Failure
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
public static boolean close(int idA)
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return false;
try
{
stopIoManager(idA);
_userDataHashByDeviceId.remove(idA);
driverL.setPermissionStatus(UsbSerialDriver.permissionStatusRequestRequired);
driverL.close();
return true;
}
catch(IOException eA)
{
return false;
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Write data to the device.
//
// Args: idA - ID number from the open command
// sourceA - byte array of data to write
// timeoutMsecA - amount of time in milliseconds to wait for the write to occur
//
// Returns: number of bytes written
//
/////////////////////////////////////////////////////////////////////////////////////////////////////
public static int write(int idA, byte[] sourceA, int timeoutMSecA)
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return 0;
try
{
return driverL.write(sourceA, timeoutMSecA);
}
catch(IOException eA)
{
return 0;
}
/*
UsbIoManager managerL = m_ioManager.get(idA);
if(managerL != null)
{
managerL.writeAsync(sourceA);
return sourceA.length;
}
else
return 0;
*/
}
public static boolean isDeviceNameValid(String nameA)
{
for (UsbSerialDriver driver: _drivers) {
if (driver.getDevice().getDeviceName() == nameA)
return true;
}
return false;
}
public static boolean isDeviceNameOpen(String nameA)
{
for (UsbSerialDriver driverL: _drivers) {
if (nameA.equals(driverL.getDevice().getDeviceName()) && driverL.permissionStatus() == UsbSerialDriver.permissionStatusOpen) {
return true;
}
}
return false;
}
/////////////////////////////////////////////////////////////////////////////////////////////////////
//
// Set the Data Terminal Ready flag on the device
//
// Args: idA - ID number from the open command
// onA - on=T, off=F
//
// Returns: T/F Success/Failure
//
////////////////////////////////////////////////////////////////////////////////////////////////////
public static boolean setDataTerminalReady(int idA, boolean onA)
{
try
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return false;
driverL.setDTR(onA);
return true;
}
catch(IOException eA)
{
return false;
}
}
////////////////////////////////////////////////////////////////////////////////////////////
//
// Set the Request to Send flag
//
// Args: idA - ID number from the open command
// onA - on=T, off=F
//
// Returns: T/F Success/Failure
//
////////////////////////////////////////////////////////////////////////////////////////////
public static boolean setRequestToSend(int idA, boolean onA)
{
try
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return false;
driverL.setRTS(onA);
return true;
}
catch(IOException eA)
{
return false;
}
}
///////////////////////////////////////////////////////////////////////////////////////////////
//
// Purge the hardware buffers based on the input and output flags
//
// Args: idA - ID number from the open command
// inputA - input buffer purge. purge=T
// outputA - output buffer purge. purge=T
//
// Returns: T/F Success/Failure
//
///////////////////////////////////////////////////////////////////////////////////////////////
public static boolean purgeBuffers(int idA, boolean inputA, boolean outputA)
{
try
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return false;
return driverL.purgeHwBuffers(inputA, outputA);
}
catch(IOException eA)
{
return false;
}
}
//////////////////////////////////////////////////////////////////////////////////////////
//
// Get the native device handle (file descriptor)
//
// Args: idA - ID number from the open command
//
// Returns: device handle
//
///////////////////////////////////////////////////////////////////////////////////////////
public static int getDeviceHandle(int idA)
{
UsbSerialDriver driverL = _findDriverByDeviceId(idA);
if (driverL == null)
return -1;
UsbDeviceConnection connectL = driverL.getDeviceConnection();
if (connectL == null)
return -1;
else
return connectL.getFileDescriptor();
}
UsbAccessory openUsbAccessory = null;
Object openAccessoryLock = new Object();
private void openAccessory(UsbAccessory usbAccessory)
{
Log.i(TAG, "openAccessory: " + usbAccessory.getSerial());
try {
synchronized(openAccessoryLock) {
if ((openUsbAccessory != null && !taiSync.isRunning()) || openUsbAccessory == null) {
openUsbAccessory = usbAccessory;
taiSync.open(_usbManager.openAccessory(usbAccessory));
}
}
} catch (IOException e) {
Log.e(TAG, "openAccessory exception: " + e);
taiSync.close();
closeAccessory(openUsbAccessory);
}
}
private void closeAccessory(UsbAccessory usbAccessory)
{
Log.i(TAG, "closeAccessory");
synchronized(openAccessoryLock) {
if (openUsbAccessory != null && usbAccessory == openUsbAccessory && taiSync.isRunning()) {
taiSync.close();
openUsbAccessory = null;
}
}
}
Object probeAccessoriesLock = new Object();
private void probeAccessories()
{
final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_USB_PERMISSION), 0);
new Thread(new Runnable() {
public void run() {
synchronized(openAccessoryLock) {
// Log.i(TAG, "probeAccessories");
UsbAccessory[] accessories = _usbManager.getAccessoryList();
if (accessories != null) {
for (UsbAccessory usbAccessory : accessories) {
if (usbAccessory == null) {
continue;
}
if (_usbManager.hasPermission(usbAccessory)) {
openAccessory(usbAccessory);
}
}
}
}
}
}).start();
}
public static String getSDCardPath() {
StorageManager storageManager = (StorageManager)_instance.getSystemService(Activity.STORAGE_SERVICE);
List<StorageVolume> volumes = storageManager.getStorageVolumes();
Method mMethodGetPath;
String path = "";
for (StorageVolume vol : volumes) {
try {
mMethodGetPath = vol.getClass().getMethod("getPath");
} catch (NoSuchMethodException e) {
e.printStackTrace();
continue;
}
try {
path = (String) mMethodGetPath.invoke(vol);
} catch (Exception e) {
e.printStackTrace();
continue;
}
if (vol.isRemovable() == true) {
Log.i(TAG, "removable sd card mounted " + path);
return path;
} else {
Log.i(TAG, "storage mounted " + path);
}
}
return "";
}
}

View File

@ -0,0 +1,286 @@
package org.mavlink.qgroundcontrol;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.Socket;
import java.net.InetAddress;
import android.os.ParcelFileDescriptor;
import android.util.Log;
public class TaiSync
{
private static final int HEADER_SIZE = 0x1C;
private static final byte PROTOCOL_REQUEST_CONNECTION = 0x00;
private static final byte PROTOCOL_VERSION = 0x01;
private static final byte PROTOCOL_CHANNEL = 0x02;
private static final byte PROTOCOL_DATA = 0x03;
private static final int VIDEO_PORT = 5600;
private static final int TAISYNC_VIDEO_PORT = 8000;
private static final int TAISYNC_SETTINGS_PORT = 8200;
private static final int TAISYNC_TELEMETRY_PORT = 8400;
private Object runLock;
private boolean running = false;
private DatagramSocket udpSocket = null;
private Socket tcpSettingsSocket = null;
private InputStream settingsInStream = null;
private OutputStream settingsOutStream = null;
private Socket tcpTelemetrySocket = null;
private InputStream telemetryInStream = null;
private OutputStream telemetryOutStream = null;
private ParcelFileDescriptor mParcelFileDescriptor;
private FileInputStream mFileInputStream;
private FileOutputStream mFileOutputStream;
private ExecutorService mThreadPool;
private byte[] mBytes = new byte[32 * 1024];
private byte vMaj = 0;
public TaiSync()
{
runLock = new Object();
mThreadPool = Executors.newFixedThreadPool(3);
}
public boolean isRunning()
{
synchronized (runLock) {
return running;
}
}
public void open(ParcelFileDescriptor parcelFileDescriptor) throws IOException
{
// Log.i("QGC_TaiSync", "Open");
synchronized (runLock) {
if (running) {
return;
}
running = true;
}
mParcelFileDescriptor = parcelFileDescriptor;
if (mParcelFileDescriptor == null) {
throw new IOException("parcelFileDescriptor is null");
}
FileDescriptor fileDescriptor = mParcelFileDescriptor.getFileDescriptor();
mFileInputStream = new FileInputStream(fileDescriptor);
mFileOutputStream = new FileOutputStream(fileDescriptor);
udpSocket = new DatagramSocket();
final InetAddress address = InetAddress.getByName("localhost");
tcpTelemetrySocket = new Socket(address, TAISYNC_TELEMETRY_PORT);
telemetryInStream = tcpTelemetrySocket.getInputStream();
telemetryOutStream = tcpTelemetrySocket.getOutputStream();
tcpSettingsSocket = new Socket(address, TAISYNC_SETTINGS_PORT);
settingsInStream = tcpSettingsSocket.getInputStream();
settingsOutStream = tcpSettingsSocket.getOutputStream();
// Request connection packet
sendTaiSyncMessage(PROTOCOL_REQUEST_CONNECTION, 0, null, 0);
// Read multiplexed data stream coming from TaiSync accessory
mThreadPool.execute(new Runnable() {
@Override
public void run()
{
int bytesRead = 0;
try {
while (bytesRead >= 0) {
synchronized (runLock) {
if (!running) {
break;
}
}
bytesRead = mFileInputStream.read(mBytes);
if (bytesRead > 0)
{
if (mBytes[3] == PROTOCOL_VERSION)
{
vMaj = mBytes[19];
// Log.i("QGC_TaiSync", "Got protocol version message vMaj = " + mBytes[19]);
sendTaiSyncMessage(PROTOCOL_VERSION, 0, null, 0);
}
else if (mBytes[3] == PROTOCOL_CHANNEL) {
int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);
// Log.i("QGC_TaiSync", "Read 2 port = " + dPort + " length = " + dLength);
sendTaiSyncMessage(PROTOCOL_CHANNEL, dPort, null, 0);
}
else if (mBytes[3] == PROTOCOL_DATA) {
int dPort = ((mBytes[4] & 0xff)<< 24) | ((mBytes[5]&0xff) << 16) | ((mBytes[6]&0xff) << 8) | (mBytes[7] &0xff);
int dLength = ((mBytes[8] & 0xff)<< 24) | ((mBytes[9]&0xff) << 16) | ((mBytes[10]&0xff) << 8) | (mBytes[11] &0xff);
int payloadOffset = HEADER_SIZE;
int payloadLength = bytesRead - payloadOffset;
byte[] sBytes = new byte[payloadLength];
System.arraycopy(mBytes, payloadOffset, sBytes, 0, payloadLength);
if (dPort == TAISYNC_VIDEO_PORT) {
DatagramPacket packet = new DatagramPacket(sBytes, sBytes.length, address, VIDEO_PORT);
udpSocket.send(packet);
} else if (dPort == TAISYNC_SETTINGS_PORT) {
settingsOutStream.write(sBytes);
} else if (dPort == TAISYNC_TELEMETRY_PORT) {
telemetryOutStream.write(sBytes);
}
}
}
}
} catch (IOException e) {
Log.e("QGC_TaiSync", "Exception: " + e);
e.printStackTrace();
} finally {
close();
}
}
});
// Read command & control packets to be sent to telemetry port
mThreadPool.execute(new Runnable() {
@Override
public void run()
{
byte[] inbuf = new byte[256];
try {
while (true) {
synchronized (runLock) {
if (!running) {
break;
}
}
int bytesRead = telemetryInStream.read(inbuf, 0, inbuf.length);
if (bytesRead > 0) {
sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_TELEMETRY_PORT, inbuf, bytesRead);
}
}
} catch (IOException e) {
Log.e("QGC_TaiSync", "Exception: " + e);
e.printStackTrace();
} finally {
close();
}
}
});
// Read incoming requests for settings socket
mThreadPool.execute(new Runnable() {
@Override
public void run()
{
byte[] inbuf = new byte[1024];
try {
while (true) {
synchronized (runLock) {
if (!running) {
break;
}
}
int bytesRead = settingsInStream.read(inbuf, 0, inbuf.length);
if (bytesRead > 0) {
sendTaiSyncMessage(PROTOCOL_DATA, TAISYNC_SETTINGS_PORT, inbuf, bytesRead);
}
}
} catch (IOException e) {
Log.e("QGC_TaiSync", "Exception: " + e);
e.printStackTrace();
} finally {
close();
}
}
});
}
private void sendTaiSyncMessage(byte protocol, int dataPort, byte[] data, int dataLen) throws IOException
{
byte portMSB = (byte)((dataPort >> 8) & 0xFF);
byte portLSB = (byte)(dataPort & 0xFF);
byte[] lA = new byte[4];
int len = HEADER_SIZE + dataLen;
// Log.i("QGC_TaiSync", "Sending to " + dataPort + " length = " + len);
byte[] buffer = new byte[len];
for (int i = 3; i >= 0; i--) {
lA[i] = (byte)(len & 0xFF);
len >>= 8;
}
byte[] header = { 0x00, 0x00, 0x00, protocol, // uint32 - protocol
0x00, 0x00, portMSB, portLSB, // uint32 - dport
lA[0], lA[1], lA[2], lA[3], // uint32 - length
0x00, 0x00, 0x00, 0x00, // uint32 - magic
0x00, 0x00, 0x00, vMaj, // uint32 - version major
0x00, 0x00, 0x00, 0x00, // uint32 - version minor
0x00, 0x00, 0x00, 0x00 }; // uint32 - padding
System.arraycopy(header, 0, buffer, 0, header.length);
if (data != null && dataLen > 0) {
System.arraycopy(data, 0, buffer, header.length, dataLen);
}
synchronized (runLock) {
mFileOutputStream.write(buffer);
}
}
public void close()
{
// Log.i("QGC_TaiSync", "Close");
synchronized (runLock) {
running = false;
}
try {
if (udpSocket != null) {
udpSocket.close();
}
} catch (Exception e) {
}
try {
if (tcpTelemetrySocket != null) {
tcpTelemetrySocket.close();
}
} catch (Exception e) {
}
try {
if (tcpSettingsSocket != null) {
tcpSettingsSocket.close();
}
} catch (Exception e) {
}
try {
if (mParcelFileDescriptor != null) {
mParcelFileDescriptor.close();
}
} catch (Exception e) {
}
udpSocket = null;
tcpSettingsSocket = null;
tcpTelemetrySocket = null;
settingsInStream = null;
settingsOutStream = null;
mParcelFileDescriptor = null;
}
}

View File

@ -0,0 +1,218 @@
package org.mavlink.qgroundcontrol;
/* Copyright 2011 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* Project home page: http://code.google.com/p/usb-serial-for-android/
*/
import com.hoho.android.usbserial.driver.*;
import java.io.IOException;
import java.nio.ByteBuffer;
import android.util.Log;
/**
* Utility class which services a {@link UsbSerialDriver} in its {@link #run()}
* method.
*
* Original author mike wakerly (opensource@hoho.com)
* Modified by Mike Goza
*/
public class UsbIoManager implements Runnable {
private static final int READ_WAIT_MILLIS = 100;
private static final int BUFSIZ = 4096;
private static final String TAG = "QGC_UsbIoManager";
private final UsbSerialDriver mDriver;
private long mUserData;
private final ByteBuffer mReadBuffer = ByteBuffer.allocate(BUFSIZ);
private final ByteBuffer mWriteBuffer = ByteBuffer.allocate(BUFSIZ);
private enum State
{
STOPPED,
RUNNING,
STOPPING
}
// Synchronized by 'this'
private State mState = State.STOPPED;
// Synchronized by 'this'
private Listener mListener;
public interface Listener
{
/**
* Called when new incoming data is available.
*/
public void onNewData(byte[] data, long userData);
/**
* Called when {@link SerialInputOutputManager#run()} aborts due to an
* error.
*/
public void onRunError(Exception e, long userData);
}
/**
* Creates a new instance with no listener.
*/
public UsbIoManager(UsbSerialDriver driver)
{
this(driver, null, 0);
Log.i(TAG, "Instance created");
}
/**
* Creates a new instance with the provided listener.
*/
public UsbIoManager(UsbSerialDriver driver, Listener listener, long userData)
{
mDriver = driver;
mListener = listener;
mUserData = userData;
}
public synchronized void setListener(Listener listener)
{
mListener = listener;
}
public synchronized Listener getListener()
{
return mListener;
}
public void writeAsync(byte[] data)
{
synchronized (mWriteBuffer)
{
mWriteBuffer.put(data);
}
}
public synchronized void stop()
{
if (getState() == State.RUNNING)
{
mState = State.STOPPING;
mUserData = 0;
}
}
private synchronized State getState()
{
return mState;
}
/**
* Continuously services the read and write buffers until {@link #stop()} is
* called, or until a driver exception is raised.
*/
@Override
public void run()
{
synchronized (this)
{
if (mState != State.STOPPED)
throw new IllegalStateException("Already running.");
mState = State.RUNNING;
}
try
{
while (true)
{
if (mState != State.RUNNING)
break;
step();
}
}
catch (Exception e)
{
final Listener listener = getListener();
if (listener != null)
listener.onRunError(e, mUserData);
}
finally
{
synchronized (this)
{
mState = State.STOPPED;
}
}
}
private void step() throws IOException
{
// Handle incoming data.
int len = mDriver.read(mReadBuffer.array(), READ_WAIT_MILLIS);
if (len > 0)
{
final Listener listener = getListener();
if (listener != null)
{
final byte[] data = new byte[len];
mReadBuffer.get(data, 0, len);
listener.onNewData(data, mUserData);
}
mReadBuffer.clear();
}
/*
// Handle outgoing data.
byte[] outBuff = null;
synchronized (mWriteBuffer)
{
if (mWriteBuffer.position() > 0)
{
len = mWriteBuffer.position();
outBuff = new byte[len];
mWriteBuffer.rewind();
mWriteBuffer.get(outBuff, 0, len);
mWriteBuffer.clear();
}
}
if (outBuff != null)
mDriver.write(outBuff, READ_WAIT_MILLIS);
*/
}
}

29
android_environment.sh Normal file
View File

@ -0,0 +1,29 @@
#!/bin/bash
#----------------------------------------------------------
# You will need:
# - Qt 5.5.x android_armv7 kit
# - Android SDK
# - Androig NDK
# - Current Java
# - ant
#----------------------------------------------------------
# Update with correct location for these
export ANDROID_HOME=~/Library/Android/sdk
export ANDROID_SDK_ROOT=~/Library/Android/sdk
export ANDROID_NDK_ROOT=~/Library/Android/sdk/ndk-bundle
export ANDROID_NDK_HOST=darwin-x86_64
export ANDROID_NDK_PLATFORM=/android-9
export ANDROID_NDK_TOOLCHAIN_PREFIX=arm-linux-androideabi
export ANDROID_NDK_TOOLCHAIN_VERSION=4.9
export ANDROID_NDK_TOOLS_PREFIX=arm-linux-androideabi
#----------------------------------------------------------
# To build it, run (replacing the path with where you have Qt installed)
#
# >source android_environment.sh
# cd ../
# mkdir android_build
# cd android_build
# >~/local/Qt/5.4/android_armv7/bin/qmake -r -spec android-g++ CONFIG+=debug ../qgroundcontrol/qgroundcontrol.pro
# >make -j24 install INSTALL_ROOT=./android-build/
# >~/local/Qt/5.4/android_armv7/bin/androiddeployqt --input ./android-libQGroundControl.so-deployment-settings.json --output ./android-build --deployment bundled --android-platform android-22 --jdk /System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Home --verbose --ant /usr/local/bin/ant
#

178
cmake/AddQtAndroidApk.cmake Normal file
View File

@ -0,0 +1,178 @@
cmake_minimum_required(VERSION 3.0)
cmake_policy(SET CMP0026 OLD) # allow use of the LOCATION target property
# store the current source directory for future use
set(QT_ANDROID_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
# check the JAVA_HOME environment variable
# (I couldn't find a way to set it from this script, it has to be defined outside)
set(JAVA_HOME $ENV{JAVA_HOME})
if(NOT JAVA_HOME)
message(FATAL_ERROR "The JAVA_HOME environment variable is not set. Please set it to the root directory of the JDK.")
endif()
# make sure that the Android toolchain is used
if(NOT ANDROID)
message(FATAL_ERROR "Trying to use the CMake Android package without the Android toolchain. Please use the provided toolchain (toolchain/android.toolchain.cmake)")
endif()
# find the Qt root directory
if(NOT Qt5Core_DIR)
find_package(Qt5Core REQUIRED)
endif()
get_filename_component(QT_ANDROID_QT_ROOT "${Qt5Core_DIR}/../../.." ABSOLUTE)
message(STATUS "Found Qt for Android: ${QT_ANDROID_QT_ROOT}")
# find the Android SDK
if(NOT QT_ANDROID_SDK_ROOT)
set(QT_ANDROID_SDK_ROOT $ENV{ANDROID_SDK})
if(NOT QT_ANDROID_SDK_ROOT)
message(FATAL_ERROR "Could not find the Android SDK. Please set either the ANDROID_SDK environment variable, or the QT_ANDROID_SDK_ROOT CMake variable to the root directory of the Android SDK")
endif()
endif()
string(REPLACE "\\" "/" QT_ANDROID_SDK_ROOT ${QT_ANDROID_SDK_ROOT}) # androiddeployqt doesn't like backslashes in paths
message(STATUS "Found Android SDK: ${QT_ANDROID_SDK_ROOT}")
# find the Android NDK
if(NOT QT_ANDROID_NDK_ROOT)
set(QT_ANDROID_NDK_ROOT $ENV{ANDROID_NDK})
if(NOT QT_ANDROID_NDK_ROOT)
set(QT_ANDROID_NDK_ROOT ${ANDROID_NDK})
if(NOT QT_ANDROID_NDK_ROOT)
message(FATAL_ERROR "Could not find the Android NDK. Please set either the ANDROID_NDK environment or CMake variable, or the QT_ANDROID_NDK_ROOT CMake variable to the root directory of the Android NDK")
endif()
endif()
endif()
string(REPLACE "\\" "/" QT_ANDROID_NDK_ROOT ${QT_ANDROID_NDK_ROOT}) # androiddeployqt doesn't like backslashes in paths
message(STATUS "Found Android NDK: ${QT_ANDROID_NDK_ROOT}")
include(CMakeParseArguments)
# define a macro to create an Android APK target
#
# example:
# add_qt_android_apk(my_app_apk my_app
# NAME "My App"
# VERSION_CODE 12
# PACKAGE_NAME "org.mycompany.myapp"
# PACKAGE_SOURCES ${CMAKE_CURRENT_LIST_DIR}/my-android-sources
# BUILDTOOLS_REVISION "23.0.3"
# KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
# KEYSTORE_PASSWORD xxxx
# DEPENDS a_linked_target "path/to/a_linked_library.so" ...
# INSTALL
#)
#
macro(add_qt_android_apk TARGET SOURCE_TARGET)
# parse the macro arguments
cmake_parse_arguments(ARG "INSTALL" "NAME;VERSION_CODE;PACKAGE_NAME;PACKAGE_SOURCES;KEYSTORE_PASSWORD;BUILDTOOLS_REVISION" "DEPENDS;KEYSTORE" ${ARGN})
# extract the full path of the source target binary
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY DEBUG_LOCATION)
else()
get_property(QT_ANDROID_APP_PATH TARGET ${SOURCE_TARGET} PROPERTY LOCATION)
endif()
# define the application name
if(ARG_NAME)
set(QT_ANDROID_APP_NAME ${ARG_NAME})
else()
set(QT_ANDROID_APP_NAME ${SOURCE_TARGET})
endif()
# define the application package name
if(ARG_PACKAGE_NAME)
set(QT_ANDROID_APP_PACKAGE_NAME ${ARG_PACKAGE_NAME})
else()
set(QT_ANDROID_APP_PACKAGE_NAME org.qtproject.${SOURCE_TARGET})
endif()
# set the Android SDK build-tools revision
if(ARG_BUILDTOOLS_REVISION)
set(QT_ANDROID_SDK_BUILDTOOLS_REVISION ${ARG_BUILDTOOLS_REVISION})
else()
set(QT_ANDROID_SDK_BUILDTOOLS_REVISION "")
endif()
# define the application source package directory
if(ARG_PACKAGE_SOURCES)
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT ${ARG_PACKAGE_SOURCES})
else()
# get version code from arguments, or generate a fixed one if not provided
set(QT_ANDROID_APP_VERSION_CODE ${ARG_VERSION_CODE})
if(NOT QT_ANDROID_APP_VERSION_CODE)
set(QT_ANDROID_APP_VERSION_CODE 1)
endif()
# try to extract the app version from the target properties, or use the version code if not provided
get_property(QT_ANDROID_APP_VERSION TARGET ${SOURCE_TARGET} PROPERTY VERSION)
if(NOT QT_ANDROID_APP_VERSION)
set(QT_ANDROID_APP_VERSION ${QT_ANDROID_APP_VERSION_CODE})
endif()
# create a subdirectory for the extra package sources
set(QT_ANDROID_APP_PACKAGE_SOURCE_ROOT "${CMAKE_CURRENT_BINARY_DIR}/package")
# generate a manifest from the template
configure_file(${QT_ANDROID_SOURCE_DIR}/AndroidManifest.xml.in ${QT_ANDROID_APP_PACKAGE_SOURCE_ROOT}/AndroidManifest.xml @ONLY)
endif()
# set the list of dependant libraries
if(ARG_DEPENDS)
foreach(LIB ${ARG_DEPENDS})
if(TARGET ${LIB})
# item is a CMake target, extract the library path
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
get_property(LIB_PATH TARGET ${LIB} PROPERTY DEBUG_LOCATION)
else()
get_property(LIB_PATH TARGET ${LIB} PROPERTY LOCATION)
endif()
set(LIB ${LIB_PATH})
endif()
if(EXTRA_LIBS)
set(EXTRA_LIBS "${EXTRA_LIBS},${LIB}")
else()
set(EXTRA_LIBS "${LIB}")
endif()
endforeach()
set(QT_ANDROID_APP_EXTRA_LIBS "\"android-extra-libs\": \"${EXTRA_LIBS}\",")
endif()
# make sure that the output directory for the Android package exists
file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI})
# create the configuration file that will feed androiddeployqt
configure_file(${QT_ANDROID_SOURCE_DIR}/qtdeploy.json.in ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json @ONLY)
# check if the apk must be signed
if(ARG_KEYSTORE)
set(SIGN_OPTIONS --release --sign ${ARG_KEYSTORE} --tsa http://timestamp.digicert.com)
if(ARG_KEYSTORE_PASSWORD)
set(SIGN_OPTIONS ${SIGN_OPTIONS} --storepass ${ARG_KEYSTORE_PASSWORD})
endif()
endif()
# check if the apk must be installed to the device
if(ARG_INSTALL)
set(INSTALL_OPTIONS --reinstall)
endif()
# specify the Android API level
if(ANDROID_NATIVE_API_LEVEL)
set(TARGET_LEVEL_OPTIONS --android-platform android-${ANDROID_NATIVE_API_LEVEL})
endif()
# create a custom command that will run the androiddeployqt utility to prepare the Android package
add_custom_target(
${TARGET}
ALL
DEPENDS ${SOURCE_TARGET}
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI} # it seems that recompiled libraries are not copied if we don't remove them first
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI}
COMMAND ${CMAKE_COMMAND} -E copy ${QT_ANDROID_APP_PATH} ${CMAKE_CURRENT_BINARY_DIR}/libs/${ANDROID_ABI}
COMMAND ${QT_ANDROID_QT_ROOT}/bin/androiddeployqt --verbose --output ${CMAKE_CURRENT_BINARY_DIR} --input ${CMAKE_CURRENT_BINARY_DIR}/qtdeploy.json --gradle ${TARGET_LEVEL_OPTIONS} ${INSTALL_OPTIONS} ${SIGN_OPTIONS}
)
endmacro()

View File

@ -0,0 +1,35 @@
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
include(CTest)
enable_testing()
if(BUILD_TESTING)
add_definitions(-DUNITTEST_BUILD)
else()
# will prevent the definition of QT_DEBUG, which enables code that uses MockLink
add_compile_definitions(QT_NO_DEBUG)
endif()
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# clang and AppleClang
add_compile_options(
-Wall
-Wextra
-Wno-address-of-packed-member # ignore for mavlink
)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
# GCC
if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.9)
add_compile_options(-fdiagnostics-color=always)
endif()
add_compile_options(
-Wall
-Wextra
)
elseif (WIN32)
add_definitions(-D_USE_MATH_DEFINES)
add_compile_options(
/wd4244 # warning C4244: '=': conversion from 'double' to 'float', possible loss of data
)
endif()

35
cmake/Git.cmake Normal file
View File

@ -0,0 +1,35 @@
find_package(Git)
if(NOT GIT_FOUND OR NOT EXISTS "${PROJECT_SOURCE_DIR}/.git")
return()
endif()
# Update submodules as needed
option(GIT_SUBMODULE "Check submodules during build" ON)
if(NOT GIT_SUBMODULE)
return()
endif()
message(STATUS "Submodule update")
execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}"
RESULT_VARIABLE GIT_SUBMODULE_RESULT)
if(NOT GIT_SUBMODULE_RESULT EQUAL "0")
message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMODULE_RESULT}, please checkout submodules")
endif()
# Fetch the necessary git variables
execute_process(COMMAND ${GIT_EXECUTABLE} describe --always --tags
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE APP_VERSION_STR
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} describe --abbrev=0
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE REL_VERSION
OUTPUT_STRIP_TRAILING_WHITESPACE)
execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --format=%aI ${REL_VERSION}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
OUTPUT_VARIABLE REL_DATE
OUTPUT_STRIP_TRAILING_WHITESPACE)

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleGetInfoString</key>
<string>${MACOSX_BUNDLE_INFO_STRING}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleLongVersionString</key>
<string>${MACOSX_BUNDLE_LONG_VERSION_STRING}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CSResourcesFileMapped</key>
<true/>
<key>LSRequiresCarbon</key>
<true/>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>

74
cmake/QGCDeploy.cmake Normal file
View File

@ -0,0 +1,74 @@
if(LINUX)
# TODO: investigate https://github.com/probonopd/linuxdeployqt
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/release/package/QGroundControl.AppImage
COMMAND ${CMAKE_SOURCE_DIR}/deploy/create_linux_appimage.sh ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_BINARY_DIR}/release/package;
DEPENDS QGroundControl
USES_TERMINAL
)
add_custom_target(appimage DEPENDS ${CMAKE_BINARY_DIR}/release/package/QGroundControl.AppImage)
elseif(APPLE)
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
find_program(MACDEPLOYQT_EXECUTABLE macdeployqt HINTS "${_qt_bin_dir}")
add_custom_target(dmg)
if(GST_FOUND)
add_custom_command(TARGET dmg
OUTPUT
COMMAND
${CMAKE_SOURCE_DIR}/tools/prepare_gstreamer_framework.sh ${CMAKE_BINARY_DIR}/gstwork/ QGroundControl.app QGroundControl
)
endif()
add_custom_command(TARGET dmg
OUTPUT
COMMAND
${MACDEPLOYQT_EXECUTABLE} $<TARGET_FILE_DIR:QGroundControl>/../.. -appstore-compliant -qmldir=${CMAKE_SOURCE_DIR}/src
COMMAND
rsync -a ${CMAKE_SOURCE_DIR}/libs/Frameworks $<TARGET_FILE_DIR:QGroundControl>/../../Contents/
COMMAND
${CMAKE_INSTALL_NAME_TOOL} -change "@rpath/SDL2.framework/Versions/A/SDL2" "@executable_path/../Frameworks/SDL2.framework/Versions/A/SDL2" $<TARGET_FILE:QGroundControl>
COMMAND
mkdir -p ${CMAKE_BINARY_DIR}/package
COMMAND
mkdir -p ${CMAKE_BINARY_DIR}/staging
COMMAND
rsync -a --delete ${CMAKE_BINARY_DIR}/QGroundControl.app ${CMAKE_BINARY_DIR}/staging
COMMAND
hdiutil create /tmp/tmp.dmg -ov -volname "QGroundControl-$${APP_VERSION_STR}" -fs HFS+ -srcfolder "staging"
COMMAND
hdiutil convert /tmp/tmp.dmg -format UDBZ -o ${CMAKE_BINARY_DIR}/package/QGroundControl.dmg
)
set_target_properties(QGroundControl PROPERTIES MACOSX_BUNDLE YES)
elseif(WIN32)
if(MSVC) # Check if we are using the Visual Studio compiler
set_target_properties(${PROJECT_NAME} PROPERTIES
WIN32_EXECUTABLE YES
LINK_FLAGS "/ENTRY:mainCRTStartup"
)
endif()
# deploy
include(Windeployqt)
windeployqt(QGroundControl "QGroundControl-installer.exe")
add_custom_command(TARGET QGroundControl POST_BUILD
COMMAND ${CMAKE_COMMAND} -E
copy_if_different $<TARGET_FILE:sdl2> $<TARGET_FILE_DIR:QGroundControl>
)
elseif(ANDROID)
include(AddQtAndroidApk)
add_qt_android_apk(QGroundControl.apk QGroundControl
PACKAGE_NAME "io.mavlink.qgroundcontrol"
#KEYSTORE ${CMAKE_CURRENT_LIST_DIR}/mykey.keystore myalias
#KEYSTORE_PASSWORD xxxxx
)
endif()

View File

@ -0,0 +1,43 @@
if(DEFINED ENV{QT_VERSION})
set(QT_VERSION $ENV{QT_VERSION})
endif()
if(NOT QT_VERSION)
# if QT version not specified then use any available version (5.12 or 5.15 only)
file(GLOB FOUND_QT_VERSIONS
LIST_DIRECTORIES true
$ENV{HOME}/Qt/5.12.*
$ENV{HOME}/Qt/5.15.*
)
if(NOT FOUND_QT_VERSIONS)
return()
endif()
list(SORT FOUND_QT_VERSIONS) # prefer 5.12
list(GET FOUND_QT_VERSIONS 0 QT_VERSION_PATH)
get_filename_component(QT_VERSION ${QT_VERSION_PATH} NAME)
endif()
if(DEFINED ENV{QT_MKSPEC})
set(QT_MKSPEC $ENV{QT_MKSPEC})
endif()
if(UNIX AND NOT APPLE AND NOT ANDROID)
set(LINUX TRUE)
endif()
if(NOT QT_MKSPEC)
if(APPLE)
set(QT_MKSPEC clang_64)
elseif(LINUX)
set(QT_MKSPEC gcc_64)
elseif(WIN32)
set(QT_MKSPEC msvc2017_64)
#set(QT_MKSPEC winrt_x64_msvc2017)
endif()
endif()
set(QT_LIBRARY_HINTS
$ENV{HOME}/Qt/${QT_VERSION}/${QT_MKSPEC}
$ENV{QT_PATH}/${QT_VERSION}/${QT_MKSPEC}
C:/Qt
)

102
cmake/Windeployqt.cmake Normal file
View File

@ -0,0 +1,102 @@
# The MIT License (MIT)
#
# Copyright (c) 2017 Nathan Osman
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
find_package(Qt5Core REQUIRED)
# Retrieve the absolute path to qmake and then use that path to find
# the windeployqt binary
get_target_property(_qmake_executable Qt5::qmake IMPORTED_LOCATION)
get_filename_component(_qt_bin_dir "${_qmake_executable}" DIRECTORY)
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${_qt_bin_dir}")
# Running this with MSVC 2015 requires CMake 3.6+
if((MSVC_VERSION VERSION_EQUAL 1900 OR MSVC_VERSION VERSION_GREATER 1900)
AND CMAKE_VERSION VERSION_LESS "3.6")
message(WARNING "Deploying with MSVC 2015+ requires CMake 3.6+")
endif()
# Add commands that copy the Qt runtime to the target's output directory after
# build and install the Qt runtime to the specified directory
function(windeployqt target directory)
# Run windeployqt immediately after build
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E
env PATH="${_qt_bin_dir}" "${WINDEPLOYQT_EXECUTABLE}"
--verbose 0
--no-compiler-runtime
--no-angle
--no-opengl-sw
\"$<TARGET_FILE:${target}>\"
)
# install(CODE ...) doesn't support generator expressions, but
# file(GENERATE ...) does - store the path in a file
file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${target}_path"
CONTENT "$<TARGET_FILE:${target}>"
)
# Before installation, run a series of commands that copy each of the Qt
# runtime files to the appropriate directory for installation
install(CODE
"
file(READ \"${CMAKE_CURRENT_BINARY_DIR}/${target}_path\" _file)
execute_process(
COMMAND \"${CMAKE_COMMAND}\" -E
env PATH=\"${_qt_bin_dir}\" \"${WINDEPLOYQT_EXECUTABLE}\"
--dry-run
--no-compiler-runtime
--no-angle
--no-opengl-sw
--list mapping
\${_file}
OUTPUT_VARIABLE _output
OUTPUT_STRIP_TRAILING_WHITESPACE
)
separate_arguments(_files WINDOWS_COMMAND \${_output})
while(_files)
list(GET _files 0 _src)
list(GET _files 1 _dest)
execute_process(
COMMAND \"${CMAKE_COMMAND}\" -E
copy \${_src} \"\${CMAKE_INSTALL_PREFIX}/${directory}/\${_dest}\"
)
list(REMOVE_AT _files 0 1)
endwhile()
"
)
# windeployqt doesn't work correctly with the system runtime libraries,
# so we fall back to one of CMake's own modules for copying them over
set(CMAKE_INSTALL_UCRT_LIBRARIES TRUE)
include(InstallRequiredSystemLibraries)
foreach(lib ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS})
get_filename_component(filename "${lib}" NAME)
add_custom_command(TARGET ${target} POST_BUILD
COMMAND "${CMAKE_COMMAND}" -E
copy_if_different "${lib}" \"$<TARGET_FILE_DIR:${target}>\"
)
endforeach()
endfunction()
mark_as_advanced(WINDEPLOYQT_EXECUTABLE)

File diff suppressed because it is too large Load Diff

17
cmake/qtdeploy.json.in Normal file
View File

@ -0,0 +1,17 @@
{
"description": "This file is to be read by androiddeployqt",
"qt": "@QT_ANDROID_QT_ROOT@",
"sdk": "@QT_ANDROID_SDK_ROOT@",
"ndk": "@QT_ANDROID_NDK_ROOT@",
"sdkBuildToolsRevision": "@QT_ANDROID_SDK_BUILDTOOLS_REVISION@",
"toolchain-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"tool-prefix": "@ANDROID_TOOLCHAIN_MACHINE_NAME@",
"toolchain-version": "@ANDROID_COMPILER_VERSION@",
"ndk-host": "@ANDROID_NDK_HOST_SYSTEM_NAME@",
"target-architecture": "@ANDROID_ABI@",
"application-binary": "@QT_ANDROID_APP_PATH@",
"android-package": "@QT_ANDROID_APP_PACKAGE_NAME@",
"android-app-name": "@QT_ANDROID_APP_NAME@",
@QT_ANDROID_APP_EXTRA_LIBS@
"android-package-source-directory": "@QT_ANDROID_APP_PACKAGE_SOURCE_ROOT@"
}

View File

@ -0,0 +1,29 @@
#include "winver.h"
IDI_ICON1 ICON DISCARDABLE "@ICON_FILE@"
VS_VERSION_INFO VERSIONINFO
FILEVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
PRODUCTVERSION @PROJECT_VERSION_MAJOR@,@PROJECT_VERSION_MINOR@,@PROJECT_VERSION_PATCH@,@PROJECT_VERSION_TWEAK@
FILEFLAGS 0x0L
FILEFLAGSMASK 0x3fL
FILEOS 0x00040004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "000004b0"
BEGIN
VALUE "CompanyName", "@COMPANY@"
VALUE "FileDescription", "@PROJECT_NAME@"
VALUE "FileVersion", "@PROJECT_VERSION@"
VALUE "LegalCopyright", "@COPYRIGHT@"
VALUE "InternalName", "@PROJECT_NAME@"
VALUE "OriginalFilename", "@PROJECT_NAME@.exe"
VALUE "ProductName", "@PROJECT_NAME@"
VALUE "ProductVersion", "@PROJECT_VERSION@"
END
END
END

5
crowdin.yml Normal file
View File

@ -0,0 +1,5 @@
files:
- source: /translations/qgc.ts
translation: /translations/qgc_source_%locale_with_underscore%.ts
- source: /translations/qgc-json.ts
translation: /translations/qgc_json_%locale_with_underscore%.ts

View File

@ -0,0 +1,301 @@
<RCC>
<qresource prefix="/InstrumentValueIcons">
<file alias="stethoscope.svg">../resources/InstrumentValueIcons/stethoscope.svg</file>
<file alias="inbox-check.svg">../resources/InstrumentValueIcons/inbox-check.svg</file>
<file alias="cheveron-up.svg">../resources/InstrumentValueIcons/cheveron-up.svg</file>
<file alias="location-shopping.svg">../resources/InstrumentValueIcons/location-shopping.svg</file>
<file alias="cheveron-right.svg">../resources/InstrumentValueIcons/cheveron-right.svg</file>
<file alias="bookmark.svg">../resources/InstrumentValueIcons/bookmark.svg</file>
<file alias="travel-case.svg">../resources/InstrumentValueIcons/travel-case.svg</file>
<file alias="user-solid-square.svg">../resources/InstrumentValueIcons/user-solid-square.svg</file>
<file alias="arrow-simple-up.svg">../resources/InstrumentValueIcons/arrow-simple-up.svg</file>
<file alias="list.svg">../resources/InstrumentValueIcons/list.svg</file>
<file alias="shuffle.svg">../resources/InstrumentValueIcons/shuffle.svg</file>
<file alias="travel-walk.svg">../resources/InstrumentValueIcons/travel-walk.svg</file>
<file alias="checkmark.svg">../resources/InstrumentValueIcons/checkmark.svg</file>
<file alias="trophy.svg">../resources/InstrumentValueIcons/trophy.svg</file>
<file alias="mood-happy-solid.svg">../resources/InstrumentValueIcons/mood-happy-solid.svg</file>
<file alias="cog.svg">../resources/InstrumentValueIcons/cog.svg</file>
<file alias="text-box.svg">../resources/InstrumentValueIcons/text-box.svg</file>
<file alias="video-camera.svg">../resources/InstrumentValueIcons/video-camera.svg</file>
<file alias="backward-step.svg">../resources/InstrumentValueIcons/backward-step.svg</file>
<file alias="view-column.svg">../resources/InstrumentValueIcons/view-column.svg</file>
<file alias="add-outline.svg">../resources/InstrumentValueIcons/add-outline.svg</file>
<file alias="battery-low.svg">../resources/InstrumentValueIcons/battery-low.svg</file>
<file alias="bookmark copy 2.svg">../resources/InstrumentValueIcons/bookmark copy 2.svg</file>
<file alias="volume-down.svg">../resources/InstrumentValueIcons/volume-down.svg</file>
<file alias="travel-taxi-cab.svg">../resources/InstrumentValueIcons/travel-taxi-cab.svg</file>
<file alias="camera.svg">../resources/InstrumentValueIcons/camera.svg</file>
<file alias="apparel.svg">../resources/InstrumentValueIcons/apparel.svg</file>
<file alias="checkmark-outline.svg">../resources/InstrumentValueIcons/checkmark-outline.svg</file>
<file alias="envelope.svg">../resources/InstrumentValueIcons/envelope.svg</file>
<file alias="flag.svg">../resources/InstrumentValueIcons/flag.svg</file>
<file alias="window-new.svg">../resources/InstrumentValueIcons/window-new.svg</file>
<file alias="arrow-simple-right.svg">../resources/InstrumentValueIcons/arrow-simple-right.svg</file>
<file alias="filter.svg">../resources/InstrumentValueIcons/filter.svg</file>
<file alias="minus-outline.svg">../resources/InstrumentValueIcons/minus-outline.svg</file>
<file alias="edit-cut.svg">../resources/InstrumentValueIcons/edit-cut.svg</file>
<file alias="volume-off.svg">../resources/InstrumentValueIcons/volume-off.svg</file>
<file alias="zoom-in.svg">../resources/InstrumentValueIcons/zoom-in.svg</file>
<file alias="pin.svg">../resources/InstrumentValueIcons/pin.svg</file>
<file alias="layers.svg">../resources/InstrumentValueIcons/layers.svg</file>
<file alias="airplane.svg">../resources/InstrumentValueIcons/airplane.svg</file>
<file alias="view-tile.svg">../resources/InstrumentValueIcons/view-tile.svg</file>
<file alias="location-marina.svg">../resources/InstrumentValueIcons/location-marina.svg</file>
<file alias="border-top.svg">../resources/InstrumentValueIcons/border-top.svg</file>
<file alias="refresh.svg">../resources/InstrumentValueIcons/refresh.svg</file>
<file alias="travel-bus.svg">../resources/InstrumentValueIcons/travel-bus.svg</file>
<file alias="add-solid.svg">../resources/InstrumentValueIcons/add-solid.svg</file>
<file alias="notifications.svg">../resources/InstrumentValueIcons/notifications.svg</file>
<file alias="indent-decrease.svg">../resources/InstrumentValueIcons/indent-decrease.svg</file>
<file alias="badge.svg">../resources/InstrumentValueIcons/badge.svg</file>
<file alias="align-center.svg">../resources/InstrumentValueIcons/align-center.svg</file>
<file alias="queue.svg">../resources/InstrumentValueIcons/queue.svg</file>
<file alias="conversation.svg">../resources/InstrumentValueIcons/conversation.svg</file>
<file alias="inbox-download.svg">../resources/InstrumentValueIcons/inbox-download.svg</file>
<file alias="cloud.svg">../resources/InstrumentValueIcons/cloud.svg</file>
<file alias="text-decoration.svg">../resources/InstrumentValueIcons/text-decoration.svg</file>
<file alias="date-add.svg">../resources/InstrumentValueIcons/date-add.svg</file>
<file alias="network.svg">../resources/InstrumentValueIcons/network.svg</file>
<file alias="list-add.svg">../resources/InstrumentValueIcons/list-add.svg</file>
<file alias="film.svg">../resources/InstrumentValueIcons/film.svg</file>
<file alias="book-reference.svg">../resources/InstrumentValueIcons/book-reference.svg</file>
<file alias="star-full.svg">../resources/InstrumentValueIcons/star-full.svg</file>
<file alias="information-outline.svg">../resources/InstrumentValueIcons/information-outline.svg</file>
<file alias="user-group.svg">../resources/InstrumentValueIcons/user-group.svg</file>
<file alias="hard-drive.svg">../resources/InstrumentValueIcons/hard-drive.svg</file>
<file alias="chart-bar.svg">../resources/InstrumentValueIcons/chart-bar.svg</file>
<file alias="box.svg">../resources/InstrumentValueIcons/box.svg</file>
<file alias="music-notes.svg">../resources/InstrumentValueIcons/music-notes.svg</file>
<file alias="bookmark copy 3.svg">../resources/InstrumentValueIcons/bookmark copy 3.svg</file>
<file alias="anchor.svg">../resources/InstrumentValueIcons/anchor.svg</file>
<file alias="mood-sad-solid.svg">../resources/InstrumentValueIcons/mood-sad-solid.svg</file>
<file alias="coffee.svg">../resources/InstrumentValueIcons/coffee.svg</file>
<file alias="mood-neutral-outline.svg">../resources/InstrumentValueIcons/mood-neutral-outline.svg</file>
<file alias="menu.svg">../resources/InstrumentValueIcons/menu.svg</file>
<file alias="fast-rewind.svg">../resources/InstrumentValueIcons/fast-rewind.svg</file>
<file alias="user-add.svg">../resources/InstrumentValueIcons/user-add.svg</file>
<file alias="mobile-devices.svg">../resources/InstrumentValueIcons/mobile-devices.svg</file>
<file alias="time.svg">../resources/InstrumentValueIcons/time.svg</file>
<file alias="subdirectory-left.svg">../resources/InstrumentValueIcons/subdirectory-left.svg</file>
<file alias="window.svg">../resources/InstrumentValueIcons/window.svg</file>
<file alias="hand-stop.svg">../resources/InstrumentValueIcons/hand-stop.svg</file>
<file alias="beverage.svg">../resources/InstrumentValueIcons/beverage.svg</file>
<file alias="volume-mute.svg">../resources/InstrumentValueIcons/volume-mute.svg</file>
<file alias="reply-all.svg">../resources/InstrumentValueIcons/reply-all.svg</file>
<file alias="location-food.svg">../resources/InstrumentValueIcons/location-food.svg</file>
<file alias="arrow-thin-left.svg">../resources/InstrumentValueIcons/arrow-thin-left.svg</file>
<file alias="folder-outline.svg">../resources/InstrumentValueIcons/folder-outline.svg</file>
<file alias="dial-pad.svg">../resources/InstrumentValueIcons/dial-pad.svg</file>
<file alias="battery-half.svg">../resources/InstrumentValueIcons/battery-half.svg</file>
<file alias="trash.svg">../resources/InstrumentValueIcons/trash.svg</file>
<file alias="notification.svg">../resources/InstrumentValueIcons/notification.svg</file>
<file alias="send.svg">../resources/InstrumentValueIcons/send.svg</file>
<file alias="station.svg">../resources/InstrumentValueIcons/station.svg</file>
<file alias="pen-tool.svg">../resources/InstrumentValueIcons/pen-tool.svg</file>
<file alias="gift.svg">../resources/InstrumentValueIcons/gift.svg</file>
<file alias="arrow-outline-down.svg">../resources/InstrumentValueIcons/arrow-outline-down.svg</file>
<file alias="ticket.svg">../resources/InstrumentValueIcons/ticket.svg</file>
<file alias="border-none.svg">../resources/InstrumentValueIcons/border-none.svg</file>
<file alias="format-italic.svg">../resources/InstrumentValueIcons/format-italic.svg</file>
<file alias="user-solid-circle.svg">../resources/InstrumentValueIcons/user-solid-circle.svg</file>
<file alias="edit-pencil.svg">../resources/InstrumentValueIcons/edit-pencil.svg</file>
<file alias="step-forward.svg">../resources/InstrumentValueIcons/step-forward.svg</file>
<file alias="edit-copy.svg">../resources/InstrumentValueIcons/edit-copy.svg</file>
<file alias="globe.svg">../resources/InstrumentValueIcons/globe.svg</file>
<file alias="arrow-thin-right.svg">../resources/InstrumentValueIcons/arrow-thin-right.svg</file>
<file alias="inbox-full.svg">../resources/InstrumentValueIcons/inbox-full.svg</file>
<file alias="mood-sad-outline.svg">../resources/InstrumentValueIcons/mood-sad-outline.svg</file>
<file alias="bug.svg">../resources/InstrumentValueIcons/bug.svg</file>
<file alias="question.svg">../resources/InstrumentValueIcons/question.svg</file>
<file alias="dots-horizontal-double.svg">../resources/InstrumentValueIcons/dots-horizontal-double.svg</file>
<file alias="format-bold.svg">../resources/InstrumentValueIcons/format-bold.svg</file>
<file alias="close-outline.svg">../resources/InstrumentValueIcons/close-outline.svg</file>
<file alias="dots-horizontal-triple.svg">../resources/InstrumentValueIcons/dots-horizontal-triple.svg</file>
<file alias="format-text-size.svg">../resources/InstrumentValueIcons/format-text-size.svg</file>
<file alias="computer-laptop.svg">../resources/InstrumentValueIcons/computer-laptop.svg</file>
<file alias="arrow-thick-down.svg">../resources/InstrumentValueIcons/arrow-thick-down.svg</file>
<file alias="cheveron-outline-down.svg">../resources/InstrumentValueIcons/cheveron-outline-down.svg</file>
<file alias="travel.svg">../resources/InstrumentValueIcons/travel.svg</file>
<file alias="usb.svg">../resources/InstrumentValueIcons/usb.svg</file>
<file alias="cheveron-down.svg">../resources/InstrumentValueIcons/cheveron-down.svg</file>
<file alias="key.svg">../resources/InstrumentValueIcons/key.svg</file>
<file alias="tools copy.svg">../resources/InstrumentValueIcons/tools copy.svg</file>
<file alias="album.svg">../resources/InstrumentValueIcons/album.svg</file>
<file alias="arrow-base-down.svg">../resources/InstrumentValueIcons/arrow-base-down.svg</file>
<file alias="copy.svg">../resources/InstrumentValueIcons/copy.svg</file>
<file alias="align-left.svg">../resources/InstrumentValueIcons/align-left.svg</file>
<file alias="explore.svg">../resources/InstrumentValueIcons/explore.svg</file>
<file alias="watch.svg">../resources/InstrumentValueIcons/watch.svg</file>
<file alias="playlist.svg">../resources/InstrumentValueIcons/playlist.svg</file>
<file alias="pause-outline.svg">../resources/InstrumentValueIcons/pause-outline.svg</file>
<file alias="location-current.svg">../resources/InstrumentValueIcons/location-current.svg</file>
<file alias="home.svg">../resources/InstrumentValueIcons/home.svg</file>
<file alias="battery-full.svg">../resources/InstrumentValueIcons/battery-full.svg</file>
<file alias="format-font-size.svg">../resources/InstrumentValueIcons/format-font-size.svg</file>
<file alias="exclamation-solid.svg">../resources/InstrumentValueIcons/exclamation-solid.svg</file>
<file alias="music-artist.svg">../resources/InstrumentValueIcons/music-artist.svg</file>
<file alias="music-album.svg">../resources/InstrumentValueIcons/music-album.svg</file>
<file alias="chart-pie.svg">../resources/InstrumentValueIcons/chart-pie.svg</file>
<file alias="photo.svg">../resources/InstrumentValueIcons/photo.svg</file>
<file alias="lock-open.svg">../resources/InstrumentValueIcons/lock-open.svg</file>
<file alias="inbox.svg">../resources/InstrumentValueIcons/inbox.svg</file>
<file alias="hot.svg">../resources/InstrumentValueIcons/hot.svg</file>
<file alias="browser-window-new.svg">../resources/InstrumentValueIcons/browser-window-new.svg</file>
<file alias="zoom-out.svg">../resources/InstrumentValueIcons/zoom-out.svg</file>
<file alias="search.svg">../resources/InstrumentValueIcons/search.svg</file>
<file alias="backward.svg">../resources/InstrumentValueIcons/backward.svg</file>
<file alias="store-front.svg">../resources/InstrumentValueIcons/store-front.svg</file>
<file alias="user.svg">../resources/InstrumentValueIcons/user.svg</file>
<file alias="lock-closed.svg">../resources/InstrumentValueIcons/lock-closed.svg</file>
<file alias="load-balancer.svg">../resources/InstrumentValueIcons/load-balancer.svg</file>
<file alias="border-all.svg">../resources/InstrumentValueIcons/border-all.svg</file>
<file alias="location-gas-station.svg">../resources/InstrumentValueIcons/location-gas-station.svg</file>
<file alias="news-paper.svg">../resources/InstrumentValueIcons/news-paper.svg</file>
<file alias="align-justified.svg">../resources/InstrumentValueIcons/align-justified.svg</file>
<file alias="color-palette.svg">../resources/InstrumentValueIcons/color-palette.svg</file>
<file alias="radio.svg">../resources/InstrumentValueIcons/radio.svg</file>
<file alias="reply.svg">../resources/InstrumentValueIcons/reply.svg</file>
<file alias="target.svg">../resources/InstrumentValueIcons/target.svg</file>
<file alias="border-vertical.svg">../resources/InstrumentValueIcons/border-vertical.svg</file>
<file alias="duplicate.svg">../resources/InstrumentValueIcons/duplicate.svg</file>
<file alias="arrow-thick-right.svg">../resources/InstrumentValueIcons/arrow-thick-right.svg</file>
<file alias="exclamation-outline.svg">../resources/InstrumentValueIcons/exclamation-outline.svg</file>
<file alias="bolt.svg">../resources/InstrumentValueIcons/bolt.svg</file>
<file alias="shopping-cart.svg">../resources/InstrumentValueIcons/shopping-cart.svg</file>
<file alias="calendar.svg">../resources/InstrumentValueIcons/calendar.svg</file>
<file alias="travel-train.svg">../resources/InstrumentValueIcons/travel-train.svg</file>
<file alias="save-disk.svg">../resources/InstrumentValueIcons/save-disk.svg</file>
<file alias="cloud-upload.svg">../resources/InstrumentValueIcons/cloud-upload.svg</file>
<file alias="dashboard.svg">../resources/InstrumentValueIcons/dashboard.svg</file>
<file alias="arrow-simple-down.svg">../resources/InstrumentValueIcons/arrow-simple-down.svg</file>
<file alias="computer-desktop.svg">../resources/InstrumentValueIcons/computer-desktop.svg</file>
<file alias="music-playlist.svg">../resources/InstrumentValueIcons/music-playlist.svg</file>
<file alias="share-01.svg">../resources/InstrumentValueIcons/share-01.svg</file>
<file alias="travel-car.svg">../resources/InstrumentValueIcons/travel-car.svg</file>
<file alias="border-horizontal.svg">../resources/InstrumentValueIcons/border-horizontal.svg</file>
<file alias="fast-forward.svg">../resources/InstrumentValueIcons/fast-forward.svg</file>
<file alias="keyboard.svg">../resources/InstrumentValueIcons/keyboard.svg</file>
<file alias="chat-bubble-dots.svg">../resources/InstrumentValueIcons/chat-bubble-dots.svg</file>
<file alias="folder.svg">../resources/InstrumentValueIcons/folder.svg</file>
<file alias="document.svg">../resources/InstrumentValueIcons/document.svg</file>
<file alias="format-underline.svg">../resources/InstrumentValueIcons/format-underline.svg</file>
<file alias="arrow-thin-down.svg">../resources/InstrumentValueIcons/arrow-thin-down.svg</file>
<file alias="cheveron-outline-left.svg">../resources/InstrumentValueIcons/cheveron-outline-left.svg</file>
<file alias="thumbs-down.svg">../resources/InstrumentValueIcons/thumbs-down.svg</file>
<file alias="stroke-width.svg">../resources/InstrumentValueIcons/stroke-width.svg</file>
<file alias="yin-yang.svg">../resources/InstrumentValueIcons/yin-yang.svg</file>
<file alias="thumbs-up.svg">../resources/InstrumentValueIcons/thumbs-up.svg</file>
<file alias="cheveron-left.svg">../resources/InstrumentValueIcons/cheveron-left.svg</file>
<file alias="radar.svg">../resources/InstrumentValueIcons/radar.svg</file>
<file alias="play.svg">../resources/InstrumentValueIcons/play.svg</file>
<file alias="bluetooth.svg">../resources/InstrumentValueIcons/bluetooth.svg</file>
<file alias="clipboard.svg">../resources/InstrumentValueIcons/clipboard.svg</file>
<file alias="mood-happy-outline.svg">../resources/InstrumentValueIcons/mood-happy-outline.svg</file>
<file alias="reload.svg">../resources/InstrumentValueIcons/reload.svg</file>
<file alias="information-solid.svg">../resources/InstrumentValueIcons/information-solid.svg</file>
<file alias="php-elephant.svg">../resources/InstrumentValueIcons/php-elephant.svg</file>
<file alias="bookmark-outline-add.svg">../resources/InstrumentValueIcons/bookmark-outline-add.svg</file>
<file alias="border-inner.svg">../resources/InstrumentValueIcons/border-inner.svg</file>
<file alias="brightness-up.svg">../resources/InstrumentValueIcons/brightness-up.svg</file>
<file alias="view-hide.svg">../resources/InstrumentValueIcons/view-hide.svg</file>
<file alias="forward-step.svg">../resources/InstrumentValueIcons/forward-step.svg</file>
<file alias="cheveron-outline-up.svg">../resources/InstrumentValueIcons/cheveron-outline-up.svg</file>
<file alias="cheveron-outline-right.svg">../resources/InstrumentValueIcons/cheveron-outline-right.svg</file>
<file alias="share.svg">../resources/InstrumentValueIcons/share.svg</file>
<file alias="border-right.svg">../resources/InstrumentValueIcons/border-right.svg</file>
<file alias="location-hotel.svg">../resources/InstrumentValueIcons/location-hotel.svg</file>
<file alias="brightness-down.svg">../resources/InstrumentValueIcons/brightness-down.svg</file>
<file alias="light-bulb.svg">../resources/InstrumentValueIcons/light-bulb.svg</file>
<file alias="location-park.svg">../resources/InstrumentValueIcons/location-park.svg</file>
<file alias="factory.svg">../resources/InstrumentValueIcons/factory.svg</file>
<file alias="stand-by.svg">../resources/InstrumentValueIcons/stand-by.svg</file>
<file alias="swap.svg">../resources/InstrumentValueIcons/swap.svg</file>
<file alias="portfolio.svg">../resources/InstrumentValueIcons/portfolio.svg</file>
<file alias="arrow-outline-right.svg">../resources/InstrumentValueIcons/arrow-outline-right.svg</file>
<file alias="tuning.svg">../resources/InstrumentValueIcons/tuning.svg</file>
<file alias="view-carousel.svg">../resources/InstrumentValueIcons/view-carousel.svg</file>
<file alias="calculator.svg">../resources/InstrumentValueIcons/calculator.svg</file>
<file alias="show-sidebar.svg">../resources/InstrumentValueIcons/show-sidebar.svg</file>
<file alias="step-backward.svg">../resources/InstrumentValueIcons/step-backward.svg</file>
<file alias="tablet.svg">../resources/InstrumentValueIcons/tablet.svg</file>
<file alias="backspace.svg">../resources/InstrumentValueIcons/backspace.svg</file>
<file alias="map.svg">../resources/InstrumentValueIcons/map.svg</file>
<file alias="arrow-simple-left.svg">../resources/InstrumentValueIcons/arrow-simple-left.svg</file>
<file alias="view-show.svg">../resources/InstrumentValueIcons/view-show.svg</file>
<file alias="adjust.svg">../resources/InstrumentValueIcons/adjust.svg</file>
<file alias="border-bottom.svg">../resources/InstrumentValueIcons/border-bottom.svg</file>
<file alias="document-add.svg">../resources/InstrumentValueIcons/document-add.svg</file>
<file alias="plugin.svg">../resources/InstrumentValueIcons/plugin.svg</file>
<file alias="navigation-more.svg">../resources/InstrumentValueIcons/navigation-more.svg</file>
<file alias="browser-window-open.svg">../resources/InstrumentValueIcons/browser-window-open.svg</file>
<file alias="flashlight.svg">../resources/InstrumentValueIcons/flashlight.svg</file>
<file alias="currency-dollar.svg">../resources/InstrumentValueIcons/currency-dollar.svg</file>
<file alias="close-solid.svg">../resources/InstrumentValueIcons/close-solid.svg</file>
<file alias="vector.svg">../resources/InstrumentValueIcons/vector.svg</file>
<file alias="paste.svg">../resources/InstrumentValueIcons/paste.svg</file>
<file alias="arrow-outline-up.svg">../resources/InstrumentValueIcons/arrow-outline-up.svg</file>
<file alias="pause.svg">../resources/InstrumentValueIcons/pause.svg</file>
<file alias="artist.svg">../resources/InstrumentValueIcons/artist.svg</file>
<file alias="hour-glass.svg">../resources/InstrumentValueIcons/hour-glass.svg</file>
<file alias="browser-window.svg">../resources/InstrumentValueIcons/browser-window.svg</file>
<file alias="border-left.svg">../resources/InstrumentValueIcons/border-left.svg</file>
<file alias="tag.svg">../resources/InstrumentValueIcons/tag.svg</file>
<file alias="translate.svg">../resources/InstrumentValueIcons/translate.svg</file>
<file alias="mood-neutral-solid.svg">../resources/InstrumentValueIcons/mood-neutral-solid.svg</file>
<file alias="pause-solid.svg">../resources/InstrumentValueIcons/pause-solid.svg</file>
<file alias="phone.svg">../resources/InstrumentValueIcons/phone.svg</file>
<file alias="heart.svg">../resources/InstrumentValueIcons/heart.svg</file>
<file alias="headphones.svg">../resources/InstrumentValueIcons/headphones.svg</file>
<file alias="servers.svg">../resources/InstrumentValueIcons/servers.svg</file>
<file alias="close.svg">../resources/InstrumentValueIcons/close.svg</file>
<file alias="directions.svg">../resources/InstrumentValueIcons/directions.svg</file>
<file alias="arrow-thick-left.svg">../resources/InstrumentValueIcons/arrow-thick-left.svg</file>
<file alias="play-outline.svg">../resources/InstrumentValueIcons/play-outline.svg</file>
<file alias="arrow-thick-up.svg">../resources/InstrumentValueIcons/arrow-thick-up.svg</file>
<file alias="code.svg">../resources/InstrumentValueIcons/code.svg</file>
<file alias="thermometer.svg">../resources/InstrumentValueIcons/thermometer.svg</file>
<file alias="location-restroom.svg">../resources/InstrumentValueIcons/location-restroom.svg</file>
<file alias="list-bullet.svg">../resources/InstrumentValueIcons/list-bullet.svg</file>
<file alias="wrench.svg">../resources/InstrumentValueIcons/wrench.svg</file>
<file alias="compose.svg">../resources/InstrumentValueIcons/compose.svg</file>
<file alias="at-symbol.svg">../resources/InstrumentValueIcons/at-symbol.svg</file>
<file alias="library.svg">../resources/InstrumentValueIcons/library.svg</file>
<file alias="view-list.svg">../resources/InstrumentValueIcons/view-list.svg</file>
<file alias="timer.svg">../resources/InstrumentValueIcons/timer.svg</file>
<file alias="window-open.svg">../resources/InstrumentValueIcons/window-open.svg</file>
<file alias="mouse.svg">../resources/InstrumentValueIcons/mouse.svg</file>
<file alias="buoy.svg">../resources/InstrumentValueIcons/buoy.svg</file>
<file alias="subdirectory-right.svg">../resources/InstrumentValueIcons/subdirectory-right.svg</file>
<file alias="volume-up.svg">../resources/InstrumentValueIcons/volume-up.svg</file>
<file alias="edit-crop.svg">../resources/InstrumentValueIcons/edit-crop.svg</file>
<file alias="screen-full.svg">../resources/InstrumentValueIcons/screen-full.svg</file>
<file alias="forward.svg">../resources/InstrumentValueIcons/forward.svg</file>
<file alias="pylon.svg">../resources/InstrumentValueIcons/pylon.svg</file>
<file alias="align-right.svg">../resources/InstrumentValueIcons/align-right.svg</file>
<file alias="border-outer.svg">../resources/InstrumentValueIcons/border-outer.svg</file>
<file alias="arrow-thin-up.svg">../resources/InstrumentValueIcons/arrow-thin-up.svg</file>
<file alias="printer.svg">../resources/InstrumentValueIcons/printer.svg</file>
<file alias="wallet.svg">../resources/InstrumentValueIcons/wallet.svg</file>
<file alias="arrow-outline-left.svg">../resources/InstrumentValueIcons/arrow-outline-left.svg</file>
<file alias="credit-card.svg">../resources/InstrumentValueIcons/credit-card.svg</file>
<file alias="repost.svg">../resources/InstrumentValueIcons/repost.svg</file>
<file alias="notifications-outline.svg">../resources/InstrumentValueIcons/notifications-outline.svg</file>
<file alias="bookmark-outline.svg">../resources/InstrumentValueIcons/bookmark-outline.svg</file>
<file alias="radar copy 2.svg">../resources/InstrumentValueIcons/radar copy 2.svg</file>
<file alias="share-alt.svg">../resources/InstrumentValueIcons/share-alt.svg</file>
<file alias="link.svg">../resources/InstrumentValueIcons/link.svg</file>
<file alias="attachment.svg">../resources/InstrumentValueIcons/attachment.svg</file>
<file alias="chart.svg">../resources/InstrumentValueIcons/chart.svg</file>
<file alias="shield.svg">../resources/InstrumentValueIcons/shield.svg</file>
<file alias="block.svg">../resources/InstrumentValueIcons/block.svg</file>
<file alias="indent-increase.svg">../resources/InstrumentValueIcons/indent-increase.svg</file>
<file alias="arrow-base-up.svg">../resources/InstrumentValueIcons/arrow-base-up.svg</file>
<file alias="minus-solid.svg">../resources/InstrumentValueIcons/minus-solid.svg</file>
<file alias="folder-outline-add.svg">../resources/InstrumentValueIcons/folder-outline-add.svg</file>
<file alias="location.svg">../resources/InstrumentValueIcons/location.svg</file>
<file alias="mic.svg">../resources/InstrumentValueIcons/mic.svg</file>
<file alias="education.svg">../resources/InstrumentValueIcons/education.svg</file>
<file alias="announcement.svg">../resources/InstrumentValueIcons/announcement.svg</file>
</qresource>
</RCC>

BIN
custom-example/README.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

27
custom-example/README.md Normal file
View File

@ -0,0 +1,27 @@
# QGroundControl Ground Control Station
## Custom Build Example
To build this sample custom version:
* Clean you build directory of any previous build
* Rename the directory from `custom-example` to `custom`
* Change to the `custom` directory
* Run `python updateqrc.py`
* Build QGC
![Custom Build Screenshot](README.jpg)
More details on what a custom build is and how to create your own can be found in the [QGC Dev Guide](https://dev.qgroundcontrol.com/en/custom_build/custom_build.html).
The main features of this example:
* Assumes an "Off The Shelf" purchased commercial vehicle. This means most vehicle setup is hidden from the user since they should mostly never need to adjust those things. They would be set up correctly by the vehicle producing company prior to sale.
* The above assumption cause the QGC UI to adjust and not show various things. Providing an even simpler experience to the user.
* The full experience continues to be available in "Advanced Mode".
* Brands the build with various custom images and custom color palette which matches corporate branding of the theoretical commercial company this build is for.
* Customizes portions of the interface such as you can see in the above screenshot which shows a custom instrument widget replacing the standard QGC ui.
* It also overrides various QGC Application settings to hide some settings the users shouldn't modify as well as adjusting defaults for others.
* The source code is fully commented to explain what and why it is doing things.
> Important Note: This custom build is not automatically built each time regular QGC code changes. This can mean that it may fall out of date with the latest changes in QGC code. This can show up as the `python updateqrc.py` steps failing due to upstream resource changes. Or possibly fail to compile because the plugin mechanism for custom builds has changed. If this happens please notify the QGC devs and they will bring it up to date. Or even better, submit a pull for the fix yourself!

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.5 KiB

Some files were not shown because too many files have changed in this diff Show More