Skip to content

Commit

Permalink
ADSB: Add Unit Test
Browse files Browse the repository at this point in the history
  • Loading branch information
HTRamsey committed Aug 22, 2024
1 parent e9b615f commit 7d4e17f
Show file tree
Hide file tree
Showing 19 changed files with 602 additions and 267 deletions.
53 changes: 53 additions & 0 deletions src/ADSB/ADSB.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#pragma once

#include <QtCore/QMetaType>
#include <QtPositioning/QGeoCoordinate>

namespace ADSB {
Q_NAMESPACE

/*=======================================================================*
|Messages |TC |Ground(still)|Ground(moving)|Airborne|
|-----------------------|----------|-------------|--------------|--------|
|Aircraft identification|1–4 |0.1 Hz |0.2 Hz |0.2 Hz |
|Surface position |5–8 |0.2 Hz |2.0 Hz |-.- Hz |
|Airborne position |9–18,20–22|-.- Hz |-.- Hz |2.0 Hz |
|Airborne velocity |19 |-.- Hz |-.- Hz |2.0 Hz |
*=======================================================================*/

/// Enum for ADSB message types.
enum MessageType {
IdentificationAndCategory = 1,
SurfacePosition = 2,
AirbornePosition = 3,
AirborneVelocity = 4,
SurveillanceAltitude = 5,
SurveillanceId = 6,
Unsupported = -1
};
Q_ENUM_NS(MessageType)

/// Enum for ADSB Info Types Available.
enum AvailableInfoType {
CallsignAvailable = 1 << 0,
LocationAvailable = 1 << 1,
AltitudeAvailable = 1 << 2,
HeadingAvailable = 1 << 3,
AlertAvailable = 1 << 4,
};
Q_FLAG_NS(AvailableInfoType)
Q_DECLARE_FLAGS(AvailableInfoTypes, AvailableInfoType)
Q_DECLARE_OPERATORS_FOR_FLAGS(AvailableInfoTypes)

struct VehicleInfo_t {
uint32_t icaoAddress;
QString callsign;
QGeoCoordinate location;
double altitude; // TODO: Use Altitude in QGeoCoordinate?
double heading;
bool alert;
AvailableInfoTypes availableFlags;
};
} // namespace ADSB

Q_DECLARE_METATYPE(ADSB::VehicleInfo_t)
150 changes: 82 additions & 68 deletions src/ADSB/ADSBTCPLink.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,85 +8,102 @@
****************************************************************************/

#include "ADSBTCPLink.h"
#include <QGCLoggingCategory.h>
// #include "DeviceInfo.h"
#include "QGCLoggingCategory.h"

#include <QtNetwork/QTcpSocket>
#include <QtCore/QTimer>
#include <QtNetwork/QTcpSocket>

QGC_LOGGING_CATEGORY(ADSBTCPLinkLog, "qgc.adsb.adsbtcplink")

ADSBTCPLink::ADSBTCPLink(const QString &hostAddress, quint16 port, QObject *parent)
ADSBTCPLink::ADSBTCPLink(const QHostAddress &hostAddress, quint16 port, QObject *parent)
: QObject(parent)
, m_socket(new QTcpSocket(this))
, m_processTimer(new QTimer(this))
, _hostAddress(hostAddress)
, _port(port)
, _socket(new QTcpSocket(this))
, _processTimer(new QTimer(this))
{
// qCDebug(ADSBTCPLinkLog) << Q_FUNC_INFO << this;

(void) connect(m_socket, &QTcpSocket::stateChanged, this, [this](QTcpSocket::SocketState state) {
#ifdef QT_DEBUG
(void) connect(_socket, &QTcpSocket::stateChanged, this, [](QTcpSocket::SocketState state) {
switch (state) {
case QTcpSocket::SocketState::UnconnectedState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket disconnected";
break;

case QTcpSocket::SocketState::ConnectingState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket connecting...";
break;

case QTcpSocket::SocketState::ConnectedState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket connected";
break;

case QTcpSocket::SocketState::ClosingState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket closing...";

default:
break;
case QTcpSocket::UnconnectedState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket disconnected";
break;
case QTcpSocket::SocketState::ConnectingState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket connecting...";
break;
case QTcpSocket::SocketState::ConnectedState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket connected";
break;
case QTcpSocket::SocketState::ClosingState:
qCDebug(ADSBTCPLinkLog) << "ADSB Socket closing...";
break;
default:
break;
}
}, Qt::AutoConnection);
#endif

(void) QObject::connect(m_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) {
qCDebug(ADSBTCPLinkLog) << error << m_socket->errorString();
emit errorOccurred(m_socket->errorString());
(void) QObject::connect(_socket, &QTcpSocket::errorOccurred, this, [this](QTcpSocket::SocketError error) {
qCDebug(ADSBTCPLinkLog) << error << _socket->errorString();
// TODO: Check if it is a critical error or not and send if the socket is stopped/recoverable
emit errorOccurred(_socket->errorString(), false);
}, Qt::AutoConnection);

(void) connect(m_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
(void) connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);

m_processTimer->setInterval(s_processInterval); // Set an interval for processing lines
(void) connect(m_processTimer, &QTimer::timeout, this, &ADSBTCPLink::_processLines);
_processTimer->setInterval(_processInterval); // Set an interval for processing lines
(void) connect(_processTimer, &QTimer::timeout, this, &ADSBTCPLink::_processLines);

m_socket->connectToHost(hostAddress, port);
init();

// qCDebug(ADSBTCPLinkLog) << Q_FUNC_INFO << this;
}

ADSBTCPLink::~ADSBTCPLink()
{
// qCDebug(ADSBTCPLinkLog) << Q_FUNC_INFO << this;
}

bool ADSBTCPLink::init()
{
// TODO: Check Address Target & Internet Availability
// QGCDeviceInfo::isInternetAvailable()

if (_hostAddress.isNull()) {
return false;
}

_socket->connectToHost(_hostAddress, _port);

return true;
}

void ADSBTCPLink::_readBytes()
{
while (m_socket && m_socket->canReadLine()) {
const QByteArray bytes = m_socket->readLine();
m_lineBuffer.append(QString::fromLocal8Bit(bytes));
while (_socket && _socket->canReadLine()) {
const QByteArray bytes = _socket->readLine();
(void) _lineBuffer.append(QString::fromLocal8Bit(bytes));
}

// Start or restart the timer to process lines
if (!m_processTimer->isActive()) {
m_processTimer->start();
if (!_processTimer->isActive()) {
_processTimer->start();
}
}

void ADSBTCPLink::_processLines()
{
int linesProcessed = 0;
while (!m_lineBuffer.isEmpty() && (linesProcessed < s_maxLinesToProcess)) {
const QString line = m_lineBuffer.takeFirst();
while (!_lineBuffer.isEmpty() && (linesProcessed < _maxLinesToProcess)) {
const QString line = _lineBuffer.takeFirst();
_parseLine(line);
++linesProcessed;
}

// Stop the timer if there are no more lines to process
if (m_lineBuffer.isEmpty()) {
m_processTimer->stop();
if (_lineBuffer.isEmpty()) {
_processTimer->stop();
}
}

Expand All @@ -101,13 +118,13 @@ void ADSBTCPLink::_parseLine(const QString &line)
}

const int msgType = line.at(4).digitValue();
if (msgType == ADSB::ADSBMessageType::Unsupported) {
qCDebug(ADSBTCPLinkLog) << "ADSB Invalid message type " << msgType;
if (msgType == ADSB::Unsupported) {
qCDebug(ADSBTCPLinkLog) << "ADSB Invalid message type" << msgType;
return;
}

// Skip unsupported mesg types to avoid parsing
if ((msgType == ADSB::ADSBMessageType::SurfacePosition) || (msgType > ADSB::ADSBMessageType::SurveillanceId)) {
if ((msgType == ADSB::SurfacePosition) || (msgType > ADSB::SurveillanceId)) {
return;
}

Expand All @@ -124,30 +141,27 @@ void ADSBTCPLink::_parseLine(const QString &line)
return;
}

ADSBVehicle::ADSBVehicleInfo_t adsbInfo;
ADSB::VehicleInfo_t adsbInfo;
adsbInfo.icaoAddress = icaoAddress;

switch (msgType) {
case ADSB::ADSBMessageType::IdentificationAndCategory:
case ADSB::ADSBMessageType::SurveillanceAltitude:
case ADSB::ADSBMessageType::SurveillanceId:
_parseAndEmitCallsign(adsbInfo, values);
break;

case ADSB::ADSBMessageType::AirbornePosition:
_parseAndEmitLocation(adsbInfo, values);
break;

case ADSB::ADSBMessageType::AirborneVelocity:
_parseAndEmitHeading(adsbInfo, values);
break;

default:
break;
case ADSB::IdentificationAndCategory:
case ADSB::SurveillanceAltitude:
case ADSB::SurveillanceId:
_parseAndEmitCallsign(adsbInfo, values);
break;
case ADSB::AirbornePosition:
_parseAndEmitLocation(adsbInfo, values);
break;
case ADSB::AirborneVelocity:
_parseAndEmitHeading(adsbInfo, values);
break;
default:
break;
}
}

void ADSBTCPLink::_parseAndEmitCallsign(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo, const QStringList &values)
void ADSBTCPLink::_parseAndEmitCallsign(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
{
if (values.size() <= 10) {
return;
Expand All @@ -159,12 +173,12 @@ void ADSBTCPLink::_parseAndEmitCallsign(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo
}

adsbInfo.callsign = callsign;
adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable;
adsbInfo.availableFlags = ADSB::CallsignAvailable;

emit adsbVehicleUpdate(adsbInfo);
}

void ADSBTCPLink::_parseAndEmitLocation(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo, const QStringList &values)
void ADSBTCPLink::_parseAndEmitLocation(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
{
if (values.size() <= 19) {
return;
Expand All @@ -177,7 +191,7 @@ void ADSBTCPLink::_parseAndEmitLocation(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo
// knowledge about Geoid shape in particular Lat, Lon. It's not worth complicating the code
QString altitudeStr = values.at(11);
if (altitudeStr.endsWith('H')) {
altitudeStr.chop(1);
(void) altitudeStr.chop(1);
}

bool altOk, latOk, lonOk, alertOk;
Expand All @@ -200,12 +214,12 @@ void ADSBTCPLink::_parseAndEmitLocation(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo
adsbInfo.location = location;
adsbInfo.altitude = altitude;
adsbInfo.alert = (alert == 1);
adsbInfo.availableFlags = ADSBVehicle::LocationAvailable | ADSBVehicle::AltitudeAvailable | ADSBVehicle::AlertAvailable;
adsbInfo.availableFlags = ADSB::LocationAvailable | ADSB::AltitudeAvailable | ADSB::AlertAvailable;

emit adsbVehicleUpdate(adsbInfo);
}

void ADSBTCPLink::_parseAndEmitHeading(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo, const QStringList &values)
void ADSBTCPLink::_parseAndEmitHeading(ADSB::VehicleInfo_t &adsbInfo, const QStringList &values)
{
if (values.size() <= 13) {
return;
Expand All @@ -218,7 +232,7 @@ void ADSBTCPLink::_parseAndEmitHeading(ADSBVehicle::ADSBVehicleInfo_t &adsbInfo,
}

adsbInfo.heading = heading;
adsbInfo.availableFlags = ADSBVehicle::HeadingAvailable;
adsbInfo.availableFlags = ADSB::HeadingAvailable;

emit adsbVehicleUpdate(adsbInfo);
}
Loading

0 comments on commit 7d4e17f

Please sign in to comment.