目录
上篇我们介绍QGC开头几个工具类,但是没有将QGCApplication因为看到里面类和文件很多都是还没接触到的,所以决定先看其他的。这篇我们看下源码中ADSB文件夹下的代码是干什么的。

何为ADSB,先来一段百度百科的解释,ADS-B[1]是广播式自动相关监视的英文缩写,它主要实施空对空监视,一般情况下,只需机载电子设备(GPS接收机、数据链收发机及其天线、驾驶舱冲突信息显示器CDTI),不需要任何地面辅助设备即可完成相关功能,装备了ADS-B[1]的飞机可通过数据链广播其自身的精确位置和其它数据(如速度、高度及飞机是否转弯、爬升或下降等)。ADS-B[1]接收机与空管系统、其它飞机的机载ADS-B[1]结合起来,在空地都能提供精确、实时的冲突信息。ADS-B[1] 是一种全新科技,它将当今空中交通管制中的三大要素通信、导航、监视重新定义。
Automatic——自动,“全天候运行”,无需职守
Dependent——相关,它只需要于依赖精确地全球卫星导航定位数据
Surveillance——监视,监视(获得)飞机位置、高度、速度、航向、识别号和其它信息
Broadcast——广播,无需应答,飞机之间或与地面站互相广播各自的数据信息
飞机上:
GPS接收机,GPS是ADS-B系统主要的数据来源。
静压管或大气计算机,可以向ADS-B提供气压高度信息。
FMS,可以向ADS-B提供航班号信息。
S模式应答机,从GPS、静压管或大气计算机、FMS获得信息并发送出去。
地面上:
ADS-B接收机:接收飞机上发送出来的信息并传送给显示系统。
显示系统:图形化显示飞机的位置信息。
下面来分析代码
这个类时候单体类,应该与后边要讲的ADSBVehicleManager对应,属于被管理者。
首先分析头文件:
- /****************************************************************************
- *
- * (c) 2009-2020 QGROUNDCONTROL PROJECT
- *
- * QGroundControl is licensed according to the terms in the file
- * COPYING.md in the root of the source code directory.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include
- #include
- #include
-
- #include "QGCMAVLink.h"
-
- class ADSBVehicle : public QObject
- {
- Q_OBJECT
-
- public:
- enum {
- CallsignAvailable = 1 << 1, //通讯呼号 2
- LocationAvailable = 1 << 2, //定位 4
- AltitudeAvailable = 1 << 3, //海拔高度 8
- HeadingAvailable = 1 << 4, //航向 16
- AlertAvailable = 1 << 5, //告警 32
- };
-
- typedef struct {
- uint32_t icaoAddress; // Required 国际民用航空组织,国际民航组织 (International Civil Aviation Organization)
- QString callsign; //通讯呼号
- QGeoCoordinate location; //定位
- double altitude; //海拔高度
- double heading; //航向
- bool alert; //告警
- uint32_t availableFlags; //可用标志
- } VehicleInfo_t;
-
- //构造函数 传入vehicleInfo结构
- ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent);
-
- //定义属性
- //还是以上的几个属性
- Q_PROPERTY(int icaoAddress READ icaoAddress CONSTANT)
- Q_PROPERTY(QString callsign READ callsign NOTIFY callsignChanged)
- Q_PROPERTY(QGeoCoordinate coordinate READ coordinate NOTIFY coordinateChanged)
- Q_PROPERTY(double altitude READ altitude NOTIFY altitudeChanged) // NaN for not available
- Q_PROPERTY(double heading READ heading NOTIFY headingChanged) // NaN for not available
- Q_PROPERTY(bool alert READ alert NOTIFY alertChanged) // Collision path
-
- int icaoAddress (void) const { return static_cast<int>(_icaoAddress); }
- QString callsign (void) const { return _callsign; }
- QGeoCoordinate coordinate (void) const { return _coordinate; }
- double altitude (void) const { return _altitude; }
- double heading (void) const { return _heading; }
- bool alert (void) const { return _alert; }
-
- void update(const VehicleInfo_t& vehicleInfo);
-
- /// check if the vehicle is expired and should be removed
- /// 检查设备是否过期,应予以删除
- bool expired();
-
- signals:
- void coordinateChanged ();
- void callsignChanged ();
- void altitudeChanged ();
- void headingChanged ();
- void alertChanged ();
-
- private:
- uint32_t _icaoAddress;
- QString _callsign;
- QGeoCoordinate _coordinate;
- double _altitude;
- double _heading;
- bool _alert;
-
- //上次更新的时间
- QElapsedTimer _lastUpdateTimer;
-
- static constexpr qint64 expirationTimeoutMs = 120000; ///< timeout with no update in ms after which the vehicle is removed.车辆被移除后,在毫秒内没有更新的超时
- ///< AirMap sends updates for each vehicle every second. AirMap每秒钟都会更新每辆车的信息
- };
-
- Q_DECLARE_METATYPE(ADSBVehicle::VehicleInfo_t)
-
头文件中主要围绕的还是那么几个属性,国际民用航空组织的地址,呼号,定位,海拔高度,航向,告警信息,还有对过期设备的管理,更新。
其中Q_DECLARE_METATYPE(ADSBVehicle::VehicleInfo_t)这个宏的作用这里说下。
Q_DECLARE_METATYPE(Type)
这个宏是为了让QMetaType知道Type这个数据类型,并提供一个默认的拷贝构造函数和析构函数。QVariant需要使用Q_DECLARE_METATYPE这个宏来定制类型。
当使用这个宏的时候要求Type是一个完整的数据类型。可以使用Q_DECLARE_OPAQUE_POINTER()来注册一个指针用于转发声明类型。
一般都把这个宏放到结构体或类的末尾【注意:这是官方说的】,如果不放到末尾也是阔以的,就放到头文件中,当你用 QVariant就要包含那个.h,个人觉得这非常不适合面向对象以及模块化编程。
看下官方例子更为直观
- struct MyStruct
- {
- int i;
- ...
- };
-
- Q_DECLARE_METATYPE(MyStruct)
- namespace MyNamespace
- {
- ...
- }
-
- Q_DECLARE_METATYPE(MyNamespace::MyStruct)
- MyStruct s;
- QVariant var;
- var.setValue(s); // copy s into the variant
-
- ...
-
- // retrieve the value
- MyStruct s2 = var.value
();
感觉就是反射。具体深入一些讲解可以看下这篇:Qt文档阅读笔记-关于Q_DECLARE_METATYPE原理以及使用_IT1995的博客-CSDN博客_q_declare_metatype
cc文件如下
- /****************************************************************************
- *
- * (c) 2009-2020 QGROUNDCONTROL PROJECT
- *
- * QGroundControl is licensed according to the terms in the file
- * COPYING.md in the root of the source code directory.
- *
- ****************************************************************************/
-
- #include "ADSBVehicle.h"
- #include "QGCLoggingCategory.h"
- #include "QGC.h"
-
- #include
- #include
-
- ADSBVehicle::ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent)
- : QObject (parent)
- , _icaoAddress (vehicleInfo.icaoAddress)
- , _altitude (qQNaN())
- , _heading (qQNaN())
- , _alert (false)
- {
- //构造函数上来先设置值然后更新
- update(vehicleInfo);
- }
-
- void ADSBVehicle::update(const VehicleInfo_t& vehicleInfo)
- {
- if (_icaoAddress != vehicleInfo.icaoAddress) {
- qCWarning(ADSBVehicleManagerLog) << "ICAO address mismatch expected:actual" << _icaoAddress << vehicleInfo.icaoAddress;
- return;
- }
- if (vehicleInfo.availableFlags & CallsignAvailable) {
- //不相等才设置并触发信号
- if (vehicleInfo.callsign != _callsign) {
- _callsign = vehicleInfo.callsign;
- emit callsignChanged();
- }
- }
- if (vehicleInfo.availableFlags & LocationAvailable) {
- if (_coordinate != vehicleInfo.location) {
- _coordinate = vehicleInfo.location;
- emit coordinateChanged();
- }
- }
- if (vehicleInfo.availableFlags & AltitudeAvailable) {
- if (!QGC::fuzzyCompare(vehicleInfo.altitude, _altitude)) {
- _altitude = vehicleInfo.altitude;
- emit altitudeChanged();
- }
- }
- if (vehicleInfo.availableFlags & HeadingAvailable) {
- if (!QGC::fuzzyCompare(vehicleInfo.heading, _heading)) {
- _heading = vehicleInfo.heading;
- emit headingChanged();
- }
- }
- if (vehicleInfo.availableFlags & AlertAvailable) {
- if (vehicleInfo.alert != _alert) {
- _alert = vehicleInfo.alert;
- emit alertChanged();
- }
- }
- _lastUpdateTimer.restart();
- }
-
- bool ADSBVehicle::expired()
- {
- //函数用来判断是否已经过了expirationTimeoutMs这个时间
- return _lastUpdateTimer.hasExpired(expirationTimeoutMs);
- }
此文件含有两个类,一个是ADSBTCPLink这个类继承自线程,主要处理对远程服务器的tcp连接,连接异常处理,已经连接后ADSB数据的获取和解析。另一个是ADSBVehicleManager,此类主要处理各个ADSBVehicle,包括了所有的设备列表和map,对数据的清理,以及接收ADSBTCPLink发出的设备更新信号。代码如下:
- /****************************************************************************
- *
- * (c) 2009-2020 QGROUNDCONTROL PROJECT
- *
- * QGroundControl is licensed according to the terms in the file
- * COPYING.md in the root of the source code directory.
- *
- ****************************************************************************/
-
- #pragma once
-
- #include "QGCToolbox.h"
- #include "QmlObjectListModel.h"
- #include "ADSBVehicle.h"
-
- #include
- #include
- #include
- #include
-
- //定义类
- class ADSBVehicleManagerSettings;
-
- //继承自thread
- ///ADSB的连接 tcp连接
- class ADSBTCPLink : public QThread
- {
- Q_OBJECT
-
- public:
- //构造函数 地址 端口号
- ADSBTCPLink(const QString& hostAddress, int port, QObject* parent);
- ~ADSBTCPLink();
-
- signals:
- //信号 设备更新和错误
- void adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo);
- void error(const QString errorMsg);
-
- protected:
- void run(void) final;
-
- private slots:
- //读取数据
- void _readBytes(void);
-
- private:
- //硬件连接
- void _hardwareConnect(void);
- void _parseLine(const QString& line);
-
- QString _hostAddress;
- int _port;
- QTcpSocket* _socket = nullptr;
- };
-
-
- ///ADSBVehicleManager继承自QGCTool
- class ADSBVehicleManager : public QGCTool {
- Q_OBJECT
-
- public:
- //构造函数
- ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox);
-
- //定义属性adsbVehicles 设备列表
- Q_PROPERTY(QmlObjectListModel* adsbVehicles READ adsbVehicles CONSTANT)
-
- //返回adsb设备列表
- QmlObjectListModel* adsbVehicles(void) { return &_adsbVehicles; }
-
- //QGCTool overrides具体可以看我讲解QGCTool那一节
- void setToolbox(QGCToolbox* toolbox) final;
-
- //槽信号处理函数
- public slots:
- void adsbVehicleUpdate (const ADSBVehicle::VehicleInfo_t vehicleInfo);
- void _tcpError (const QString errorMsg);
-
- private slots:
- void _cleanupStaleVehicles(void);
-
- private:
- QmlObjectListModel _adsbVehicles; //设备列表
- QMap<uint32_t, ADSBVehicle*> _adsbICAOMap; //ADSBVehicle的map
- QTimer _adsbVehicleCleanupTimer; //清理定时器
- ADSBTCPLink* _tcpLink = nullptr; //tcp网络连接
- };
cc文件如下:
- /****************************************************************************
- *
- * (c) 2009-2020 QGROUNDCONTROL PROJECT
- *
- * QGroundControl is licensed according to the terms in the file
- * COPYING.md in the root of the source code directory.
- *
- ****************************************************************************/
-
- #include "ADSBVehicleManager.h"
- #include "QGCLoggingCategory.h"
- #include "QGCApplication.h"
- #include "SettingsManager.h"
- #include "ADSBVehicleManagerSettings.h"
-
- #include
-
- ADSBVehicleManager::ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox)
- : QGCTool(app, toolbox)
- {
- }
-
- //设置工具箱父类
- void ADSBVehicleManager::setToolbox(QGCToolbox* toolbox)
- {
- QGCTool::setToolbox(toolbox);
-
- //绑定槽函数处理设备的清理
- connect(&_adsbVehicleCleanupTimer, &QTimer::timeout, this, &ADSBVehicleManager::_cleanupStaleVehicles);
- _adsbVehicleCleanupTimer.setSingleShot(false);
- _adsbVehicleCleanupTimer.start(1000);
-
- //获取此类的setting设置
- ADSBVehicleManagerSettings* settings = qgcApp()->toolbox()->settingsManager()->adsbVehicleManagerSettings();
- //如果设置的adsb服务器可以连接
- if (settings->adsbServerConnectEnabled()->rawValue().toBool()) {
- //创建tcplink 网络地址和网络端口
- _tcpLink = new ADSBTCPLink(settings->adsbServerHostAddress()->rawValue().toString(), settings->adsbServerPort()->rawValue().toInt(), this);
- //创建槽函数处理设备更新和错误
- connect(_tcpLink, &ADSBTCPLink::adsbVehicleUpdate, this, &ADSBVehicleManager::adsbVehicleUpdate, Qt::QueuedConnection);
- connect(_tcpLink, &ADSBTCPLink::error, this, &ADSBVehicleManager::_tcpError, Qt::QueuedConnection);
- }
- }
-
- void ADSBVehicleManager::_cleanupStaleVehicles()
- {
- // Remove all expired ADSB vehicles移除所有过期的ADSB设备
- for (int i=_adsbVehicles.count()-1; i>=0; i--) {
- ADSBVehicle* adsbVehicle = _adsbVehicles.value
(i); - if (adsbVehicle->expired()) {
- qCDebug(ADSBVehicleManagerLog) << "Expired" << QStringLiteral("%1").arg(adsbVehicle->icaoAddress(), 0, 16);
- _adsbVehicles.removeAt(i);
- _adsbICAOMap.remove(adsbVehicle->icaoAddress());
- adsbVehicle->deleteLater();
- }
- }
- }
-
- void ADSBVehicleManager::adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo)
- {
- uint32_t icaoAddress = vehicleInfo.icaoAddress;
-
- //更细操作如果包含就行找到进行更新
- //如果不包含并且定位功能可用就创建ADSBVehicle然后插入到容器中
- if (_adsbICAOMap.contains(icaoAddress)) {
- _adsbICAOMap[icaoAddress]->update(vehicleInfo);
- } else {
- if (vehicleInfo.availableFlags & ADSBVehicle::LocationAvailable) {
- ADSBVehicle* adsbVehicle = new ADSBVehicle(vehicleInfo, this);
- _adsbICAOMap[icaoAddress] = adsbVehicle;
- _adsbVehicles.append(adsbVehicle);
- }
- }
- }
-
- void ADSBVehicleManager::_tcpError(const QString errorMsg)
- {
- qgcApp()->showAppMessage(tr("ADSB Server Error: %1").arg(errorMsg));
- }
-
-
- ADSBTCPLink::ADSBTCPLink(const QString& hostAddress, int port, QObject* parent)
- : QThread (parent)
- , _hostAddress (hostAddress)
- , _port (port)
- {
- //构造函数中开启线程
- moveToThread(this);
- start();
- }
-
-
- ADSBTCPLink::~ADSBTCPLink(void)
- {
- //析构函数处理数据清理关闭socket
- if (_socket) {
- QObject::disconnect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
- _socket->disconnectFromHost();
- _socket->deleteLater();
- _socket = nullptr;
- }
- quit();
- wait();
- }
-
-
- void ADSBTCPLink::run(void)
- {
- _hardwareConnect();
- exec();
- }
-
- void ADSBTCPLink::_hardwareConnect()
- {
- _socket = new QTcpSocket();
-
- QObject::connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
-
- _socket->connectToHost(_hostAddress, static_cast
(_port)); -
- // Give the socket a second to connect to the other side otherwise error out
- // 给套接字一秒钟时间连接到另一边,否则会出错
- if (!_socket->waitForConnected(1000)) {
- qCDebug(ADSBVehicleManagerLog) << "ADSB Socket failed to connect";
- emit error(_socket->errorString());
- delete _socket;
- _socket = nullptr;
- return;
- }
-
- qCDebug(ADSBVehicleManagerLog) << "ADSB Socket connected";
- }
-
- void ADSBTCPLink::_readBytes(void)
- {
- if (_socket) {
- QByteArray bytes = _socket->readLine();
- _parseLine(QString::fromLocal8Bit(bytes));
- }
- }
-
-
- ///此函数处理adsb传递过来的数据
- void ADSBTCPLink::_parseLine(const QString& line)
- {
- //传输数据以MSG开头
- if (line.startsWith(QStringLiteral("MSG"))) {
- qCDebug(ADSBVehicleManagerLog) << "ADSB SBS-1" << line;
-
- //以逗号分割
- QStringList values = line.split(QStringLiteral(","));
-
- if (values[1] == QStringLiteral("3")) {
- bool icaoOk, altOk, latOk, lonOk;
-
- //16进制获取空管地址
- uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
- //获取海拔
- int modeCAltitude = values[11].toInt(&altOk);
- //获取经度
- double lat = values[14].toDouble(&latOk);
- //获取维度
- double lon = values[15].toDouble(&lonOk);
- //获取呼号
- QString callsign = values[10];
-
- //处理错误情况
- if (!icaoOk || !altOk || !latOk || !lonOk) {
- return;
- }
- if (lat == 0 && lon == 0) {
- return;
- }
-
- //计算高度
- double altitude = modeCAltitude / 3.048;
- //构造地理坐标
- QGeoCoordinate location(lat, lon);
-
- //构建结构起
- ADSBVehicle::VehicleInfo_t adsbInfo;
- adsbInfo.icaoAddress = icaoAddress;
- adsbInfo.callsign = callsign;
- adsbInfo.location = location;
- adsbInfo.altitude = altitude;
- adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable | ADSBVehicle::LocationAvailable | ADSBVehicle::AltitudeAvailable;
- //触发更新信号给manager
- emit adsbVehicleUpdate(adsbInfo);
- } else if (values[1] == QStringLiteral("4")) {
- bool icaoOk, headingOk;
-
- //16进制获取空管地址
- uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
- //获取航向
- double heading = values[13].toDouble(&headingOk);
-
- if (!icaoOk || !headingOk) {
- return;
- }
-
- ADSBVehicle::VehicleInfo_t adsbInfo;
- adsbInfo.icaoAddress = icaoAddress;
- adsbInfo.heading = heading;
- adsbInfo.availableFlags = ADSBVehicle::HeadingAvailable;
- emit adsbVehicleUpdate(adsbInfo);
- } else if (values[1] == QStringLiteral("1")) {
- bool icaoOk;
- //16进制获取空管地址
- uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
- if (!icaoOk) {
- return;
- }
-
- ADSBVehicle::VehicleInfo_t adsbInfo;
- adsbInfo.icaoAddress = icaoAddress;
- adsbInfo.callsign = values[10];
- adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable;
- emit adsbVehicleUpdate(adsbInfo);
- }
- }
- }
这篇文章篇幅较短,主要介绍了ADSB系统,以及ADSB在GGC中是如何处理的,主要模块有三个一个是vehicle是ADSB设备单体,一个是link处理与远程服务器的连接,一个是manger管理所有的vehicle。还有一个setting类因为在setting文件夹下并且继承了setting的某些类所以准备放到讲解setting的时候在讲解。
下篇我们按照顺序对Aimap文件夹下的代码进行解析。