Unverified Commit b8a4746d authored by Don Gagne's avatar Don Gagne Committed by GitHub

Merge pull request #7114 from Williangalvani/udpNmea

Add support to receive NMEA streams via UDP
parents 7930364f 6fbd0351
......@@ -18,6 +18,7 @@ Note: This file only contains high level features or important fixes.
* 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
......
......@@ -210,6 +210,7 @@
<file alias="QGCLogoBlack">resources/QGCLogoBlack.svg</file>
<file alias="QGCLogoFull">resources/QGCLogoFull.svg</file>
<file alias="QGCLogoWhite">resources/QGCLogoWhite.svg</file>
<file alias="QGCLogoArrow">resources/QGCLogoArrow.svg</file>
<file alias="QGroundControlConnect">resources/QGroundControlConnect.svg</file>
<file alias="rtl.svg">resources/rtl.svg</file>
<file alias="SplashScreen">resources/SplashScreen.png</file>
......
......@@ -620,6 +620,7 @@ HEADERS += \
src/comm/QGCMAVLink.h \
src/comm/TCPLink.h \
src/comm/UDPLink.h \
src/comm/UdpIODevice.h \
src/uas/UAS.h \
src/uas/UASInterface.h \
src/uas/UASMessageHandler.h \
......@@ -820,6 +821,7 @@ SOURCES += \
src/comm/QGCMAVLink.cc \
src/comm/TCPLink.cc \
src/comm/UDPLink.cc \
src/comm/UdpIODevice.cc \
src/main.cc \
src/uas/UAS.cc \
src/uas/UASMessageHandler.cc \
......
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 72 72"
style="enable-background:new 0 0 72 72;"
xml:space="preserve"
sodipodi:docname="QGCLogoArrow.svg"
inkscape:version="0.92.2 2405546, 2018-03-11"><metadata
id="metadata23"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
id="defs21" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="2560"
inkscape:window-height="1016"
id="namedview19"
showgrid="false"
inkscape:zoom="9.2709556"
inkscape:cx="27.235609"
inkscape:cy="41.171517"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1"
inkscape:current-layer="g16" />
<style
type="text/css"
id="style2">
.st0{fill:#C72B27;}
.st1{fill:#7F0036;}
.st2{fill:#EE3424;}
</style>
<g
id="g16">
<g
id="g8">
<polygon
class="st0"
points="35.5,2.118 35.5,53.691 1.118,70.882 "
id="polygon4"
style="fill:#4b2c6d;fill-opacity:1" />
<path
class="st1"
d="M35,4.236v49.146L2.236,69.764L35,4.236 M36,0L0,72l36-18V0L36,0z"
id="path6"
style="fill:#4e005b;fill-opacity:1" />
</g><g
inkscape:groupmode="layer"
id="layer1"
inkscape:label="Layer 1" />
<g
id="g14">
<polygon
class="st2"
points="36.5,53.691 36.5,2.118 70.882,70.882 "
id="polygon10"
style="fill:#4b2c6d;fill-opacity:1" />
<path
class="st1"
d="M37,4.236l32.764,65.528L37,53.382V4.236 M36,0v54l36,18L36,0L36,0z"
id="path12"
style="fill:#4e005b;fill-opacity:1" />
</g>
<rect
x="22.746561"
y="24.02066"
class="st0"
width="28.493719"
height="27.410166"
id="rect4"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.55064428"
ry="13.705083"
rx="12.432203" /><path
inkscape:connector-curvature="0"
class="st1"
d="M 40.481217,11.731933 36.001273,1.7752157 30.56639,14.31297 c -2.380355,5.491254 -3.980916,6.040006 -6.02939,11.992023 l -6.68916,19.435928 c -1.938321,5.631958 6.119569,7.70955 12.262119,7.69678 l 14.249711,-0.02962 c 6.142548,-0.01277 15.907094,-3.46401 13.095514,-8.720704 L 50.751722,32.154194 C 47.930248,26.967975 41.501216,17.642415 40.481217,11.731937 Z m 4.195168,36.993208 -2.047336,-1.982796 c -1.530884,0.889792 -3.329787,1.389962 -5.243395,1.389962 v 0 c -5.32005,0 -9.721346,-3.910319 -10.256821,-8.932608 h 4.458929 c 0.536033,2.83574 3.234107,4.929625 6.391559,4.633207 0.459377,-0.03685 0.918755,-0.148479 1.339525,-0.296416 l -2.085944,-2.020186 3.138427,-3.039493 2.277304,2.18709 c 0.306066,-0.611799 0.516451,-1.278874 0.593106,-1.982796 0.306065,-3.057916 -1.875558,-5.670937 -4.803041,-6.171649 v -4.318367 c 5.185763,0.519137 9.242945,4.762721 9.242945,9.933489 v 0 c 0,2.094428 -0.669761,4.058799 -1.817926,5.652513 l 1.951656,1.890133 z"
id="path6-4"
style="fill:#4b2c6d;fill-opacity:1;stroke-width:0.55064428"
sodipodi:nodetypes="ccsssssscscccccccccccccccccccc" /><path
inkscape:connector-curvature="0"
class="st2"
d="m 36.256515,25.317959 h -0.07666 C 29.788296,25.85552 24.698214,30.803568 24.124133,36.993643 v 0.07424 0.05582 c 0,0.407506 0.344672,0.722889 0.746418,0.722889 0.401745,0 0.727393,-0.314842 0.746418,-0.685499 v 0 c 0.478402,-5.50404 5.013426,-9.896646 10.678151,-10.378391 v 0 c 0.38272,-0.01842 0.688787,-0.314841 0.70781,-0.685497 v -0.03685 c 0.01903,-0.427014 -0.32509,-0.742397 -0.746418,-0.742397 z"
id="path8"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.55064428"
sodipodi:nodetypes="ccccsscccccccc" /><path
inkscape:connector-curvature="0"
class="st2"
d="m 36.256515,28.190548 h -0.07666 -0.01902 c -4.803042,0.53756 -8.611232,4.318367 -9.070609,8.821518 v 0.09266 c 0,0.407506 0.344672,0.722889 0.746419,0.722889 0.401744,0 0.727394,-0.314842 0.746418,-0.685499 v -0.01842 c 0.478401,-3.928744 3.673902,-7.042477 7.71206,-7.505797 v 0 c 0.382721,-0.01843 0.688787,-0.314842 0.70781,-0.685499 v -0.03685 c 0.01903,-0.389623 -0.325089,-0.705006 -0.746418,-0.705006 z"
id="path10"
style="fill:#ffffff;fill-opacity:1;stroke-width:0.55064428"
sodipodi:nodetypes="ccccsscccccccc" /><path
inkscape:connector-curvature="0"
class="st2"
d="m 36.256515,31.081561 c -0.03805,0 -0.09567,0 -0.133728,0.01842 -3.080796,0.500171 -5.511412,2.872589 -6.027863,5.856266 v 0.01842 c 0,0.03685 -0.01902,0.11109 -0.01902,0.129512 0,0.407506 0.325089,0.72289 0.746419,0.72289 h 0.09567 c 0.344672,-0.03685 0.631714,-0.314841 0.650738,-0.64865 v -0.07424 c 0.440354,-2.316603 2.334376,-4.151461 4.745969,-4.577392 h 0.03805 c 0.363697,-0.03685 0.650739,-0.333808 0.650739,-0.685498 v -0.03685 c 0.01846,-0.38908 -0.325649,-0.722888 -0.746979,-0.722888 z"
id="path12-3"
style="fill:#ffffff;fill-opacity:0.94117647;stroke-width:0.55064428"
sodipodi:nodetypes="cccssscccccsccc" /><path
style="stroke-width:0.07627118"
d=""
id="path3787"
inkscape:connector-curvature="0" /><path
style="fill:#f9f9f9;stroke-width:0.07627118"
d="M 53.932087,62.909526 36.065561,53.97513 v -0.286093 -0.286093 h 0.495125 0.495125 l 16.322672,8.160876 c 8.977469,4.488482 16.322671,8.143734 16.322671,8.122782 0,-0.02095 -2.623661,-5.285326 -5.830358,-11.698612 C 60.074913,50.396352 57.997025,46.159936 57.916026,45.847349 57.764496,45.262578 57.646791,45.027524 56.487074,42.993798 55.068374,40.505904 50.43602,30.787387 49.888293,29.776328 48.941925,28.02941 48.367416,26.879769 47.005323,24.076673 46.196292,22.411741 45.26452,20.776789 42.03838,14.313961 39.818704,9.8673513 37.56677,5.2574683 37.034083,4.0697769 l -0.968522,-2.1594389 0.0042,-0.8151374 0.0042,-0.81513738 17.885612,35.76058178 c 9.837086,19.66832 17.875118,35.770188 17.862295,35.781929 -0.01282,0.01174 -8.063253,-3.99913 -17.889841,-8.913048 z"
id="path3789"
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccccssscssssscccsccc" /><path
style="fill:#f9f9f9;stroke-width:0.07627118"
d="M 0.14606269,71.803368 C 0.14838922,71.771906 8.2121638,55.63197 18.065561,35.936843 L 35.98083,0.12752085 l 0.0042,0.82931483 0.0042,0.82931482 -0.696473,1.6113631 C 34.873857,4.3668219 28.155843,17.890262 18.432636,37.337254 9.5425965,55.117862 2.2841976,69.680902 2.3028613,69.699566 2.3215249,69.71823 9.6854814,66.059125 18.667209,61.568222 l 16.330414,-8.165278 h 0.495833 0.495834 v 0.286093 0.286093 l -17.885637,8.94272 c -9.8370994,4.918497 -17.90277724,8.942721 -17.92372803,8.942721 -0.0209508,0 -0.0361888,-0.02574 -0.0338623,-0.0572 z"
id="path3791"
inkscape:connector-curvature="0" /></g>
</svg>
\ No newline at end of file
......@@ -35,6 +35,7 @@ Map {
property string mapName: 'defaultMap'
property bool isSatelliteMap: activeMapType.name.indexOf("Satellite") > -1 || activeMapType.name.indexOf("Hybrid") > -1
property var gcsPosition: QGroundControl.qgcPositionManger.gcsPosition
property var gcsHeading: QGroundControl.qgcPositionManger.gcsHeading
property bool userPanned: false ///< true: the user has manually panned the map
property bool allowGCSLocationCenter: false ///< true: map will center/zoom to gcs location one time
property bool allowVehicleLocationCenter: false ///< true: map will center/zoom to vehicle location one time
......@@ -134,12 +135,18 @@ Map {
coordinate: gcsPosition
sourceItem: Image {
source: "/res/QGCLogoFull"
id: mapItemImage
source: isNaN(gcsHeading) ? "/res/QGCLogoFull" : "/res/QGCLogoArrow"
mipmap: true
antialiasing: true
fillMode: Image.PreserveAspectFit
height: ScreenTools.defaultFontPixelHeight * 1.75
height: ScreenTools.defaultFontPixelHeight * (isNaN(gcsHeading) ? 1.75 : 2.5 )
sourceSize.height: height
transform: Rotation {
origin.x: mapItemImage.width / 2
origin.y: mapItemImage.height / 2
angle: isNaN(gcsHeading) ? 0 : gcsHeading
}
}
}
} // Map
......@@ -14,6 +14,7 @@
QGCPositionManager::QGCPositionManager(QGCApplication* app, QGCToolbox* toolbox)
: QGCTool (app, toolbox)
, _updateInterval (0)
, _gcsHeading (NAN)
, _currentSource (NULL)
, _defaultSource (NULL)
, _nmeaSource (NULL)
......@@ -49,8 +50,19 @@ void QGCPositionManager::setToolbox(QGCToolbox *toolbox)
void QGCPositionManager::setNmeaSourceDevice(QIODevice* device)
{
// stop and release _nmeaSource
if (_nmeaSource) {
_nmeaSource->stopUpdates();
disconnect(_nmeaSource);
// if _currentSource is pointing there, point to null
if (_currentSource == _nmeaSource){
_currentSource = nullptr;
}
delete _nmeaSource;
_nmeaSource = nullptr;
}
_nmeaSource = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode, this);
_nmeaSource->setDevice(device);
......@@ -60,6 +72,7 @@ void QGCPositionManager::setNmeaSourceDevice(QIODevice* device)
void QGCPositionManager::_positionUpdated(const QGeoPositionInfo &update)
{
QGeoCoordinate newGCSPosition = QGeoCoordinate();
qreal newGCSHeading = update.attribute(QGeoPositionInfo::Direction);
if (update.isValid()) {
// Note that gcsPosition filters out possible crap values
......@@ -71,6 +84,10 @@ void QGCPositionManager::_positionUpdated(const QGeoPositionInfo &update)
_gcsPosition = newGCSPosition;
emit gcsPositionChanged(_gcsPosition);
}
if (newGCSHeading != _gcsHeading) {
_gcsHeading = newGCSHeading;
emit gcsHeadingChanged(_gcsHeading);
}
emit positionInfoUpdated(update);
}
......
......@@ -25,7 +25,8 @@ public:
QGCPositionManager(QGCApplication* app, QGCToolbox* toolbox);
~QGCPositionManager();
Q_PROPERTY(QGeoCoordinate gcsPosition READ gcsPosition NOTIFY gcsPositionChanged)
Q_PROPERTY(QGeoCoordinate gcsPosition READ gcsPosition NOTIFY gcsPositionChanged)
Q_PROPERTY(qreal gcsHeading READ gcsHeading NOTIFY gcsHeadingChanged)
enum QGCPositionSource {
Simulated,
......@@ -36,6 +37,8 @@ public:
QGeoCoordinate gcsPosition(void) { return _gcsPosition; }
qreal gcsHeading() { return _gcsHeading; }
void setPositionSource(QGCPositionSource source);
int updateInterval() const;
......@@ -50,11 +53,13 @@ private slots:
signals:
void gcsPositionChanged(QGeoCoordinate gcsPosition);
void gcsHeadingChanged(qreal gcsHeading);
void positionInfoUpdated(QGeoPositionInfo update);
private:
int _updateInterval;
QGeoCoordinate _gcsPosition;
qreal _gcsHeading;
QGeoPositionInfoSource* _currentSource;
QGeoPositionInfoSource* _defaultSource;
......
......@@ -72,5 +72,11 @@
"shortDescription": "UDP target host port for autoconnect",
"type": "uint32",
"defaultValue": 14550
},
{
"name": "nmeaUdpPort",
"shortDescription": "Udp port to receive NMEA streams",
"type": "uint32",
"defaultValue": 14401
}
]
......@@ -23,6 +23,7 @@ DECLARE_SETTINGSFACT(AutoConnectSettings, autoConnectUDP)
DECLARE_SETTINGSFACT(AutoConnectSettings, udpListenPort)
DECLARE_SETTINGSFACT(AutoConnectSettings, udpTargetHostIP)
DECLARE_SETTINGSFACT(AutoConnectSettings, udpTargetHostPort)
DECLARE_SETTINGSFACT(AutoConnectSettings, nmeaUdpPort)
DECLARE_SETTINGSFACT_NO_FUNC(AutoConnectSettings, autoConnectPixhawk)
{
......
......@@ -31,5 +31,5 @@ public:
DEFINE_SETTINGFACT(udpListenPort)
DEFINE_SETTINGFACT(udpTargetHostIP)
DEFINE_SETTINGFACT(udpTargetHostPort)
DEFINE_SETTINGFACT(nmeaUdpPort)
};
......@@ -24,6 +24,7 @@ add_library(comm
SerialLink.cc
TCPLink.cc
UDPLink.cc
UdpIODevice.cc
${EXTRA_SRC}
......
......@@ -479,6 +479,27 @@ void LinkManager::_updateAutoConnectLinks(void)
createConnectedLink(config);
emit linkConfigurationsChanged();
}
#ifndef __mobile__
// check to see if nmea gps is configured for UDP input, if so, set it up to connect
if (_autoConnectSettings->autoConnectNmeaPort()->cookedValueString() == "UDP Port") {
if (_nmeaSocket.localPort() != _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt()
|| _nmeaSocket.state() != UdpIODevice::BoundState) {
qCDebug(LinkManagerLog) << "Changing port for UDP NMEA stream";
_nmeaSocket.close();
_nmeaSocket.bind(QHostAddress::AnyIPv4, _autoConnectSettings->nmeaUdpPort()->rawValue().toUInt());
_toolbox->qgcPositionManager()->setNmeaSourceDevice(&_nmeaSocket);
}
//close serial port
if (_nmeaPort) {
_nmeaPort->close();
delete _nmeaPort;
_nmeaPort = nullptr;
_nmeaDeviceName = "";
}
} else {
_nmeaSocket.close();
}
#endif
#ifndef NO_SERIAL_LINK
QStringList currentPorts;
......@@ -513,6 +534,7 @@ void LinkManager::_updateAutoConnectLinks(void)
#ifndef NO_SERIAL_LINK
#ifndef __mobile__
// check to see if nmea gps is configured for current Serial port, if so, set it up to connect
if (portInfo.systemLocation().trimmed() == _autoConnectSettings->autoConnectNmeaPort()->cookedValueString()) {
if (portInfo.systemLocation().trimmed() != _nmeaDeviceName) {
_nmeaDeviceName = portInfo.systemLocation().trimmed();
......
......@@ -25,6 +25,7 @@
#include "MAVLinkProtocol.h"
#if !defined(__mobile__)
#include "LogReplayLink.h"
#include "UdpIODevice.h"
#endif
#include "QmlObjectListModel.h"
......@@ -241,6 +242,7 @@ private:
QString _nmeaDeviceName;
QSerialPort* _nmeaPort;
uint32_t _nmeaBaud;
UdpIODevice _nmeaSocket;
#endif
#endif
};
......
/****************************************************************************
*
* (c) 2009-2018 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.
*
****************************************************************************/
#include "UdpIODevice.h"
#include <algorithm>
UdpIODevice::UdpIODevice(QObject *parent) : QUdpSocket(parent)
{
// this might cause data to be available only after a second readyRead() signal
connect(this, &QUdpSocket::readyRead, this, &UdpIODevice::_readAvailableData);
}
bool UdpIODevice::canReadLine() const
{
return _buffer.indexOf('\n') > -1;
}
qint64 UdpIODevice::readLineData(char *data, qint64 maxSize)
{
int length = _buffer.indexOf('\n') + 1; // add 1 to include the '\n'
if (length == 0) {
return 0;
}
length = std::min(length, static_cast<int>(maxSize));
// copy lines to output
std::copy(_buffer.data(), _buffer.data() + length, data);
// trim buffer to remove consumed line
_buffer = _buffer.right(_buffer.size() - length);
// return number of bytes read
return length;
}
void UdpIODevice::_readAvailableData() {
while (hasPendingDatagrams()) {
int previousSize = _buffer.size();
_buffer.resize(static_cast<int>(_buffer.size() + pendingDatagramSize()));
readDatagram((_buffer.data() + previousSize), pendingDatagramSize());
}
}
/****************************************************************************
*
* (c) 2009-2018 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.
*
****************************************************************************/
#pragma once
#include <QUdpSocket>
/**
* @brief QUdpSocket implementation of canReadLine() readLineData() in server mode.
* The UdpIODevice class works almost exactly as a QUdpSocket, but
* also implements canReadLine() and readLineData() while in the bound state.
* Regular QUdpSocket only allows to use these QIODevice interfaces when using
* connectToHost(), which means it is working as a client instead of server.
*
**/
class UdpIODevice: public QUdpSocket
{
Q_OBJECT
public:
UdpIODevice(QObject *parent = nullptr);
bool canReadLine() const;
qint64 readLineData(char *data, qint64 maxSize);
private slots:
void _readAvailableData();
private:
QByteArray _buffer;
};
......@@ -52,6 +52,9 @@ QGCView {
property bool _isTCP: _isGst && _videoSource === QGroundControl.settingsManager.videoSettings.tcpVideoSource
property bool _isMPEGTS: _isGst && _videoSource === QGroundControl.settingsManager.videoSettings.mpegtsVideoSource
property string gpsDisabled: "Disabled"
property string gpsUdpPort: "UDP Port"
readonly property real _internalWidthRatio: 0.8
QGCPalette { id: qgcPal }
......@@ -504,7 +507,6 @@ QGCView {
Layout.preferredWidth: _comboFieldWidth
model: ListModel {
ListElement { text: "disabled" }
}
onActivated: {
......@@ -513,18 +515,26 @@ QGCView {
}
}
Component.onCompleted: {
model.append({text: gpsDisabled})
model.append({text: gpsUdpPort})
for (var i in QGroundControl.linkManager.serialPorts) {
nmeaPortCombo.model.append({text:QGroundControl.linkManager.serialPorts[i]})
}
var index = nmeaPortCombo.find(QGroundControl.settingsManager.autoConnectSettings.autoConnectNmeaPort.valueString);
nmeaPortCombo.currentIndex = index;
if (QGroundControl.linkManager.serialPorts.length === 0) {
nmeaPortCombo.model.append({text: "Serial <none available>"})
}
}
}
QGCLabel {
visible: nmeaPortCombo.currentText !== gpsUdpPort && nmeaPortCombo.currentText !== gpsDisabled
text: qsTr("NMEA GPS Baudrate")
}
QGCComboBox {
visible: nmeaPortCombo.currentText !== gpsUdpPort && nmeaPortCombo.currentText !== gpsDisabled
id: nmeaBaudCombo
Layout.preferredWidth: _comboFieldWidth
model: [4800, 9600, 19200, 38400, 57600, 115200]
......@@ -539,6 +549,16 @@ QGCView {
nmeaBaudCombo.currentIndex = index;
}
}
QGCLabel {
text: qsTr("NMEA stream UDP port")
visible: nmeaPortCombo.currentText === gpsUdpPort
}
FactTextField {
visible: nmeaPortCombo.currentText === gpsUdpPort
Layout.preferredWidth: _valueFieldWidth
fact: QGroundControl.settingsManager.autoConnectSettings.nmeaUdpPort
}
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment