QGroundControl
Ground Control Station for MAVLink Drones
Loading...
Searching...
No Matches
ADSBTCPLink.cc
Go to the documentation of this file.
1#include "ADSBTCPLink.h"
2// #include "DeviceInfo.h"
4
5#include <QtCore/QTimer>
6#include <QtNetwork/QTcpSocket>
7
8QGC_LOGGING_CATEGORY(ADSBTCPLinkLog, "ADSB.ADSBTCPLink")
9
10ADSBTCPLink::ADSBTCPLink(const QHostAddress &hostAddress, quint16 port, QObject *parent)
11 : QObject(parent)
12 , _hostAddress(hostAddress)
13 , _port(port)
14 , _socket(new QTcpSocket(this))
15 , _processTimer(new QTimer(this))
16{
17 if (ADSBTCPLinkLog().isDebugEnabled()) {
18 (void) connect(_socket, &QTcpSocket::stateChanged, this, [](QTcpSocket::SocketState state) {
19 switch (state) {
20 case QTcpSocket::UnconnectedState:
21 qCDebug(ADSBTCPLinkLog) << "ADSB Socket disconnected";
22 break;
23 case QTcpSocket::SocketState::ConnectingState:
24 qCDebug(ADSBTCPLinkLog) << "ADSB Socket connecting...";
25 break;
26 case QTcpSocket::SocketState::ConnectedState:
27 qCDebug(ADSBTCPLinkLog) << "ADSB Socket connected";
28 break;
29 case QTcpSocket::SocketState::ClosingState:
30 qCDebug(ADSBTCPLinkLog) << "ADSB Socket closing...";
31 break;
32 default:
33 break;
34 }
35 }, Qt::AutoConnection);
36 }
37
38 (void) QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) {
39 qCDebug(ADSBTCPLinkLog) << error << _socket->errorString();
40 // TODO: Check if it is a critical error or not and send if the socket is stopped/recoverable
41 emit errorOccurred(_socket->errorString(), false);
42 }, Qt::AutoConnection);
43
44 (void) connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
45
46 _processTimer->setInterval(_processInterval); // Set an interval for processing lines
47 (void) connect(_processTimer, &QTimer::timeout, this, &ADSBTCPLink::_processLines);
48
49 // qCDebug(ADSBTCPLinkLog) << Q_FUNC_INFO << this;
50}
51
53{
54 // qCDebug(ADSBTCPLinkLog) << Q_FUNC_INFO << this;
55}
56
58{
59 /* if (!QGCDeviceInfo::isInternetAvailable()) {
60 return false;
61 } */
62
63 if (_hostAddress.isNull()) {
64 return false;
65 }
66
67 _socket->connectToHost(_hostAddress, _port);
68
69 return true;
70}
71
72void ADSBTCPLink::_readBytes()
73{
74 while (_socket && _socket->canReadLine()) {
75 const QByteArray bytes = _socket->readLine();
76 (void) _lineBuffer.append(QString::fromLocal8Bit(bytes));
77 }
78
79 // Start or restart the timer to process lines
80 if (!_processTimer->isActive()) {
81 _processTimer->start();
82 }
83}
84
85void ADSBTCPLink::_processLines()
86{
87 int linesProcessed = 0;
88 while (!_lineBuffer.isEmpty() && (linesProcessed < _maxLinesToProcess)) {
89 const QString line = _lineBuffer.takeFirst();
90 _parseLine(line);
91 ++linesProcessed;
92 }
93
94 // Stop the timer if there are no more lines to process
95 if (_lineBuffer.isEmpty()) {
96 _processTimer->stop();
97 }
98}
99
100void ADSBTCPLink::_parseLine(const QString &line)
101{
102 if (line.size() <= 4) {
103 return;
104 }
105
106 if (!line.startsWith(QStringLiteral("MSG"))) {
107 return;
108 }
109
110 const int msgType = line.at(4).digitValue();
111 if (msgType == ADSB::Unsupported) {
112 qCDebug(ADSBTCPLinkLog) << "ADSB Invalid message type" << msgType;
113 return;
114 }
115
116 // Skip unsupported mesg types to avoid parsing
117 if ((msgType == ADSB::SurfacePosition) || (msgType > ADSB::SurveillanceId)) {
118 return;
119 }
120
121 qCDebug(ADSBTCPLinkLog) << "ADSB SBS-1" << line;
122
123 const QStringList values = line.split(QChar(','));
124 if (values.size() <= 4) {
125 return;
126 }
127
128 bool icaoOk;
129 const uint32_t icaoAddress = values.at(4).toUInt(&icaoOk, 16);
130 if (!icaoOk) {
131 return;
132 }
133
134 ADSB::VehicleInfo_t adsbInfo;
135 adsbInfo.icaoAddress = icaoAddress;
136
137 switch (msgType) {
141 _parseAndEmitCallsign(adsbInfo, values);
142 break;
144 _parseAndEmitLocation(adsbInfo, values);
145 break;
147 _parseAndEmitHeading(adsbInfo, values);
148 break;
149 default:
150 break;
151 }
152}
153
154void ADSBTCPLink::_parseAndEmitCallsign(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
155{
156 if (values.size() <= 10) {
157 return;
158 }
159
160 const QString callsign = values.at(10).trimmed();
161 if (callsign.isEmpty()) {
162 return;
163 }
164
165 adsbInfo.callsign = callsign;
167
168 emit adsbVehicleUpdate(adsbInfo);
169}
170
171void ADSBTCPLink::_parseAndEmitLocation(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
172{
173 if (values.size() <= 19) {
174 return;
175 }
176
177 // Altitude is either Barometric - based on pressure, in ft
178 // or HAE - as reported by GPS - based on WGS84 Ellipsoid, in ft
179 // If altitude ends with H, we have HAE
180 // There's a slight difference between Barometric alt and HAE, but it would require
181 // knowledge about Geoid shape in particular Lat, Lon. It's not worth complicating the code
182 QString altitudeStr = values.at(11);
183 if (altitudeStr.endsWith('H')) {
184 (void) altitudeStr.chop(1);
185 }
186
187 bool altOk, latOk, lonOk, alertOk;
188 const int modeCAltitude = altitudeStr.toInt(&altOk);
189 const double lat = values.at(14).toDouble(&latOk);
190 const double lon = values.at(15).toDouble(&lonOk);
191 const int alert = values.at(19).toInt(&alertOk);
192
193 if (!altOk || !latOk || !lonOk || !alertOk) {
194 return;
195 }
196
197 if (qFuzzyIsNull(lat) && qFuzzyIsNull(lon)) {
198 return;
199 }
200
201 const double altitude = modeCAltitude * 0.3048;
202 const QGeoCoordinate location(lat, lon, altitude);
203
204 adsbInfo.location = location;
205 adsbInfo.alert = (alert == 1);
207
208 emit adsbVehicleUpdate(adsbInfo);
209}
210
211void ADSBTCPLink::_parseAndEmitHeading(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
212{
213 if (values.size() <= 13) {
214 return;
215 }
216
217 bool headingOk = false, speedOk = false;
218 const double heading = values.at(13).toDouble(&headingOk);
219 const double speedKnots = values.at(12).toDouble(&speedOk);
220 if (!headingOk || !speedOk) {
221 return;
222 }
223
224 adsbInfo.heading = heading;
225 adsbInfo.velocity = speedKnots * 0.514444;
227
228 if (values.size() > 16) {
229 bool vertOk = false;
230 const double verticalRate = values.at(16).toDouble(&vertOk);
231 if (vertOk) {
232 adsbInfo.verticalVel = verticalRate * 0.00508;
234 }
235 }
236
237 emit adsbVehicleUpdate(adsbInfo);
238}
Error error
#define QGC_LOGGING_CATEGORY(name, categoryStr)
@ Unsupported
Definition ADSB.h:28
@ SurfacePosition
Definition ADSB.h:23
@ AirborneVelocity
Definition ADSB.h:25
@ AirbornePosition
Definition ADSB.h:24
@ IdentificationAndCategory
Definition ADSB.h:22
@ SurveillanceId
Definition ADSB.h:27
@ SurveillanceAltitude
Definition ADSB.h:26
@ AlertAvailable
Definition ADSB.h:41
@ VelocityAvailable
Definition ADSB.h:37
@ CallsignAvailable
Definition ADSB.h:38
@ AltitudeAvailable
Definition ADSB.h:35
@ LocationAvailable
Definition ADSB.h:34
@ VerticalVelAvailable
Definition ADSB.h:40
@ HeadingAvailable
Definition ADSB.h:36
uint32_t icaoAddress
Definition ADSB.h:49
double velocity
Definition ADSB.h:54
AvailableInfoTypes availableFlags
Definition ADSB.h:48
double verticalVel
Definition ADSB.h:55
double heading
Definition ADSB.h:52
QString callsign
Definition ADSB.h:50
QGeoCoordinate location
Definition ADSB.h:51