• 机器人地面站-[QGroundControl源码解析]-[3]-[ADSB]


    目录

    前言

    一.ADSBVehicle

    二.ADSBVehicleManager

    总结


    前言

    上篇我们介绍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接收机:接收飞机上发送出来的信息并传送给显示系统。
    显示系统:图形化显示飞机的位置信息。 

    下面来分析代码

    一.ADSBVehicle

    这个类时候单体类,应该与后边要讲的ADSBVehicleManager对应,属于被管理者。

     首先分析头文件:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include
    11. #include
    12. #include
    13. #include "QGCMAVLink.h"
    14. class ADSBVehicle : public QObject
    15. {
    16. Q_OBJECT
    17. public:
    18. enum {
    19. CallsignAvailable = 1 << 1, //通讯呼号 2
    20. LocationAvailable = 1 << 2, //定位 4
    21. AltitudeAvailable = 1 << 3, //海拔高度 8
    22. HeadingAvailable = 1 << 4, //航向 16
    23. AlertAvailable = 1 << 5, //告警 32
    24. };
    25. typedef struct {
    26. uint32_t icaoAddress; // Required 国际民用航空组织,国际民航组织 (International Civil Aviation Organization)
    27. QString callsign; //通讯呼号
    28. QGeoCoordinate location; //定位
    29. double altitude; //海拔高度
    30. double heading; //航向
    31. bool alert; //告警
    32. uint32_t availableFlags; //可用标志
    33. } VehicleInfo_t;
    34. //构造函数 传入vehicleInfo结构
    35. ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent);
    36. //定义属性
    37. //还是以上的几个属性
    38. Q_PROPERTY(int icaoAddress READ icaoAddress CONSTANT)
    39. Q_PROPERTY(QString callsign READ callsign NOTIFY callsignChanged)
    40. Q_PROPERTY(QGeoCoordinate coordinate READ coordinate NOTIFY coordinateChanged)
    41. Q_PROPERTY(double altitude READ altitude NOTIFY altitudeChanged) // NaN for not available
    42. Q_PROPERTY(double heading READ heading NOTIFY headingChanged) // NaN for not available
    43. Q_PROPERTY(bool alert READ alert NOTIFY alertChanged) // Collision path
    44. int icaoAddress (void) const { return static_cast<int>(_icaoAddress); }
    45. QString callsign (void) const { return _callsign; }
    46. QGeoCoordinate coordinate (void) const { return _coordinate; }
    47. double altitude (void) const { return _altitude; }
    48. double heading (void) const { return _heading; }
    49. bool alert (void) const { return _alert; }
    50. void update(const VehicleInfo_t& vehicleInfo);
    51. /// check if the vehicle is expired and should be removed
    52. /// 检查设备是否过期,应予以删除
    53. bool expired();
    54. signals:
    55. void coordinateChanged ();
    56. void callsignChanged ();
    57. void altitudeChanged ();
    58. void headingChanged ();
    59. void alertChanged ();
    60. private:
    61. uint32_t _icaoAddress;
    62. QString _callsign;
    63. QGeoCoordinate _coordinate;
    64. double _altitude;
    65. double _heading;
    66. bool _alert;
    67. //上次更新的时间
    68. QElapsedTimer _lastUpdateTimer;
    69. static constexpr qint64 expirationTimeoutMs = 120000; ///< timeout with no update in ms after which the vehicle is removed.车辆被移除后,在毫秒内没有更新的超时
    70. ///< AirMap sends updates for each vehicle every second. AirMap每秒钟都会更新每辆车的信息
    71. };
    72. 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,个人觉得这非常不适合面向对象以及模块化编程。

    看下官方例子更为直观

    1. struct MyStruct
    2. {
    3. int i;
    4. ...
    5. };
    6. Q_DECLARE_METATYPE(MyStruct)
    1. namespace MyNamespace
    2. {
    3. ...
    4. }
    5. Q_DECLARE_METATYPE(MyNamespace::MyStruct)
    1. MyStruct s;
    2. QVariant var;
    3. var.setValue(s); // copy s into the variant
    4. ...
    5. // retrieve the value
    6. MyStruct s2 = var.value();

    感觉就是反射。具体深入一些讲解可以看下这篇:Qt文档阅读笔记-关于Q_DECLARE_METATYPE原理以及使用_IT1995的博客-CSDN博客_q_declare_metatype

    cc文件如下

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "ADSBVehicle.h"
    10. #include "QGCLoggingCategory.h"
    11. #include "QGC.h"
    12. #include
    13. #include
    14. ADSBVehicle::ADSBVehicle(const VehicleInfo_t& vehicleInfo, QObject* parent)
    15. : QObject (parent)
    16. , _icaoAddress (vehicleInfo.icaoAddress)
    17. , _altitude (qQNaN())
    18. , _heading (qQNaN())
    19. , _alert (false)
    20. {
    21. //构造函数上来先设置值然后更新
    22. update(vehicleInfo);
    23. }
    24. void ADSBVehicle::update(const VehicleInfo_t& vehicleInfo)
    25. {
    26. if (_icaoAddress != vehicleInfo.icaoAddress) {
    27. qCWarning(ADSBVehicleManagerLog) << "ICAO address mismatch expected:actual" << _icaoAddress << vehicleInfo.icaoAddress;
    28. return;
    29. }
    30. if (vehicleInfo.availableFlags & CallsignAvailable) {
    31. //不相等才设置并触发信号
    32. if (vehicleInfo.callsign != _callsign) {
    33. _callsign = vehicleInfo.callsign;
    34. emit callsignChanged();
    35. }
    36. }
    37. if (vehicleInfo.availableFlags & LocationAvailable) {
    38. if (_coordinate != vehicleInfo.location) {
    39. _coordinate = vehicleInfo.location;
    40. emit coordinateChanged();
    41. }
    42. }
    43. if (vehicleInfo.availableFlags & AltitudeAvailable) {
    44. if (!QGC::fuzzyCompare(vehicleInfo.altitude, _altitude)) {
    45. _altitude = vehicleInfo.altitude;
    46. emit altitudeChanged();
    47. }
    48. }
    49. if (vehicleInfo.availableFlags & HeadingAvailable) {
    50. if (!QGC::fuzzyCompare(vehicleInfo.heading, _heading)) {
    51. _heading = vehicleInfo.heading;
    52. emit headingChanged();
    53. }
    54. }
    55. if (vehicleInfo.availableFlags & AlertAvailable) {
    56. if (vehicleInfo.alert != _alert) {
    57. _alert = vehicleInfo.alert;
    58. emit alertChanged();
    59. }
    60. }
    61. _lastUpdateTimer.restart();
    62. }
    63. bool ADSBVehicle::expired()
    64. {
    65. //函数用来判断是否已经过了expirationTimeoutMs这个时间
    66. return _lastUpdateTimer.hasExpired(expirationTimeoutMs);
    67. }

    二.ADSBVehicleManager

    此文件含有两个类,一个是ADSBTCPLink这个类继承自线程,主要处理对远程服务器的tcp连接,连接异常处理,已经连接后ADSB数据的获取和解析。另一个是ADSBVehicleManager,此类主要处理各个ADSBVehicle,包括了所有的设备列表和map,对数据的清理,以及接收ADSBTCPLink发出的设备更新信号。代码如下:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #pragma once
    10. #include "QGCToolbox.h"
    11. #include "QmlObjectListModel.h"
    12. #include "ADSBVehicle.h"
    13. #include
    14. #include
    15. #include
    16. #include
    17. //定义类
    18. class ADSBVehicleManagerSettings;
    19. //继承自thread
    20. ///ADSB的连接 tcp连接
    21. class ADSBTCPLink : public QThread
    22. {
    23. Q_OBJECT
    24. public:
    25. //构造函数 地址 端口号
    26. ADSBTCPLink(const QString& hostAddress, int port, QObject* parent);
    27. ~ADSBTCPLink();
    28. signals:
    29. //信号 设备更新和错误
    30. void adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo);
    31. void error(const QString errorMsg);
    32. protected:
    33. void run(void) final;
    34. private slots:
    35. //读取数据
    36. void _readBytes(void);
    37. private:
    38. //硬件连接
    39. void _hardwareConnect(void);
    40. void _parseLine(const QString& line);
    41. QString _hostAddress;
    42. int _port;
    43. QTcpSocket* _socket = nullptr;
    44. };
    45. ///ADSBVehicleManager继承自QGCTool
    46. class ADSBVehicleManager : public QGCTool {
    47. Q_OBJECT
    48. public:
    49. //构造函数
    50. ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox);
    51. //定义属性adsbVehicles 设备列表
    52. Q_PROPERTY(QmlObjectListModel* adsbVehicles READ adsbVehicles CONSTANT)
    53. //返回adsb设备列表
    54. QmlObjectListModel* adsbVehicles(void) { return &_adsbVehicles; }
    55. //QGCTool overrides具体可以看我讲解QGCTool那一节
    56. void setToolbox(QGCToolbox* toolbox) final;
    57. //槽信号处理函数
    58. public slots:
    59. void adsbVehicleUpdate (const ADSBVehicle::VehicleInfo_t vehicleInfo);
    60. void _tcpError (const QString errorMsg);
    61. private slots:
    62. void _cleanupStaleVehicles(void);
    63. private:
    64. QmlObjectListModel _adsbVehicles; //设备列表
    65. QMap<uint32_t, ADSBVehicle*> _adsbICAOMap; //ADSBVehicle的map
    66. QTimer _adsbVehicleCleanupTimer; //清理定时器
    67. ADSBTCPLink* _tcpLink = nullptr; //tcp网络连接
    68. };

    cc文件如下:

    1. /****************************************************************************
    2. *
    3. * (c) 2009-2020 QGROUNDCONTROL PROJECT
    4. *
    5. * QGroundControl is licensed according to the terms in the file
    6. * COPYING.md in the root of the source code directory.
    7. *
    8. ****************************************************************************/
    9. #include "ADSBVehicleManager.h"
    10. #include "QGCLoggingCategory.h"
    11. #include "QGCApplication.h"
    12. #include "SettingsManager.h"
    13. #include "ADSBVehicleManagerSettings.h"
    14. #include
    15. ADSBVehicleManager::ADSBVehicleManager(QGCApplication* app, QGCToolbox* toolbox)
    16. : QGCTool(app, toolbox)
    17. {
    18. }
    19. //设置工具箱父类
    20. void ADSBVehicleManager::setToolbox(QGCToolbox* toolbox)
    21. {
    22. QGCTool::setToolbox(toolbox);
    23. //绑定槽函数处理设备的清理
    24. connect(&_adsbVehicleCleanupTimer, &QTimer::timeout, this, &ADSBVehicleManager::_cleanupStaleVehicles);
    25. _adsbVehicleCleanupTimer.setSingleShot(false);
    26. _adsbVehicleCleanupTimer.start(1000);
    27. //获取此类的setting设置
    28. ADSBVehicleManagerSettings* settings = qgcApp()->toolbox()->settingsManager()->adsbVehicleManagerSettings();
    29. //如果设置的adsb服务器可以连接
    30. if (settings->adsbServerConnectEnabled()->rawValue().toBool()) {
    31. //创建tcplink 网络地址和网络端口
    32. _tcpLink = new ADSBTCPLink(settings->adsbServerHostAddress()->rawValue().toString(), settings->adsbServerPort()->rawValue().toInt(), this);
    33. //创建槽函数处理设备更新和错误
    34. connect(_tcpLink, &ADSBTCPLink::adsbVehicleUpdate, this, &ADSBVehicleManager::adsbVehicleUpdate, Qt::QueuedConnection);
    35. connect(_tcpLink, &ADSBTCPLink::error, this, &ADSBVehicleManager::_tcpError, Qt::QueuedConnection);
    36. }
    37. }
    38. void ADSBVehicleManager::_cleanupStaleVehicles()
    39. {
    40. // Remove all expired ADSB vehicles移除所有过期的ADSB设备
    41. for (int i=_adsbVehicles.count()-1; i>=0; i--) {
    42. ADSBVehicle* adsbVehicle = _adsbVehicles.value(i);
    43. if (adsbVehicle->expired()) {
    44. qCDebug(ADSBVehicleManagerLog) << "Expired" << QStringLiteral("%1").arg(adsbVehicle->icaoAddress(), 0, 16);
    45. _adsbVehicles.removeAt(i);
    46. _adsbICAOMap.remove(adsbVehicle->icaoAddress());
    47. adsbVehicle->deleteLater();
    48. }
    49. }
    50. }
    51. void ADSBVehicleManager::adsbVehicleUpdate(const ADSBVehicle::VehicleInfo_t vehicleInfo)
    52. {
    53. uint32_t icaoAddress = vehicleInfo.icaoAddress;
    54. //更细操作如果包含就行找到进行更新
    55. //如果不包含并且定位功能可用就创建ADSBVehicle然后插入到容器中
    56. if (_adsbICAOMap.contains(icaoAddress)) {
    57. _adsbICAOMap[icaoAddress]->update(vehicleInfo);
    58. } else {
    59. if (vehicleInfo.availableFlags & ADSBVehicle::LocationAvailable) {
    60. ADSBVehicle* adsbVehicle = new ADSBVehicle(vehicleInfo, this);
    61. _adsbICAOMap[icaoAddress] = adsbVehicle;
    62. _adsbVehicles.append(adsbVehicle);
    63. }
    64. }
    65. }
    66. void ADSBVehicleManager::_tcpError(const QString errorMsg)
    67. {
    68. qgcApp()->showAppMessage(tr("ADSB Server Error: %1").arg(errorMsg));
    69. }
    70. ADSBTCPLink::ADSBTCPLink(const QString& hostAddress, int port, QObject* parent)
    71. : QThread (parent)
    72. , _hostAddress (hostAddress)
    73. , _port (port)
    74. {
    75. //构造函数中开启线程
    76. moveToThread(this);
    77. start();
    78. }
    79. ADSBTCPLink::~ADSBTCPLink(void)
    80. {
    81. //析构函数处理数据清理关闭socket
    82. if (_socket) {
    83. QObject::disconnect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
    84. _socket->disconnectFromHost();
    85. _socket->deleteLater();
    86. _socket = nullptr;
    87. }
    88. quit();
    89. wait();
    90. }
    91. void ADSBTCPLink::run(void)
    92. {
    93. _hardwareConnect();
    94. exec();
    95. }
    96. void ADSBTCPLink::_hardwareConnect()
    97. {
    98. _socket = new QTcpSocket();
    99. QObject::connect(_socket, &QTcpSocket::readyRead, this, &ADSBTCPLink::_readBytes);
    100. _socket->connectToHost(_hostAddress, static_cast(_port));
    101. // Give the socket a second to connect to the other side otherwise error out
    102. // 给套接字一秒钟时间连接到另一边,否则会出错
    103. if (!_socket->waitForConnected(1000)) {
    104. qCDebug(ADSBVehicleManagerLog) << "ADSB Socket failed to connect";
    105. emit error(_socket->errorString());
    106. delete _socket;
    107. _socket = nullptr;
    108. return;
    109. }
    110. qCDebug(ADSBVehicleManagerLog) << "ADSB Socket connected";
    111. }
    112. void ADSBTCPLink::_readBytes(void)
    113. {
    114. if (_socket) {
    115. QByteArray bytes = _socket->readLine();
    116. _parseLine(QString::fromLocal8Bit(bytes));
    117. }
    118. }
    119. ///此函数处理adsb传递过来的数据
    120. void ADSBTCPLink::_parseLine(const QString& line)
    121. {
    122. //传输数据以MSG开头
    123. if (line.startsWith(QStringLiteral("MSG"))) {
    124. qCDebug(ADSBVehicleManagerLog) << "ADSB SBS-1" << line;
    125. //以逗号分割
    126. QStringList values = line.split(QStringLiteral(","));
    127. if (values[1] == QStringLiteral("3")) {
    128. bool icaoOk, altOk, latOk, lonOk;
    129. //16进制获取空管地址
    130. uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
    131. //获取海拔
    132. int modeCAltitude = values[11].toInt(&altOk);
    133. //获取经度
    134. double lat = values[14].toDouble(&latOk);
    135. //获取维度
    136. double lon = values[15].toDouble(&lonOk);
    137. //获取呼号
    138. QString callsign = values[10];
    139. //处理错误情况
    140. if (!icaoOk || !altOk || !latOk || !lonOk) {
    141. return;
    142. }
    143. if (lat == 0 && lon == 0) {
    144. return;
    145. }
    146. //计算高度
    147. double altitude = modeCAltitude / 3.048;
    148. //构造地理坐标
    149. QGeoCoordinate location(lat, lon);
    150. //构建结构起
    151. ADSBVehicle::VehicleInfo_t adsbInfo;
    152. adsbInfo.icaoAddress = icaoAddress;
    153. adsbInfo.callsign = callsign;
    154. adsbInfo.location = location;
    155. adsbInfo.altitude = altitude;
    156. adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable | ADSBVehicle::LocationAvailable | ADSBVehicle::AltitudeAvailable;
    157. //触发更新信号给manager
    158. emit adsbVehicleUpdate(adsbInfo);
    159. } else if (values[1] == QStringLiteral("4")) {
    160. bool icaoOk, headingOk;
    161. //16进制获取空管地址
    162. uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
    163. //获取航向
    164. double heading = values[13].toDouble(&headingOk);
    165. if (!icaoOk || !headingOk) {
    166. return;
    167. }
    168. ADSBVehicle::VehicleInfo_t adsbInfo;
    169. adsbInfo.icaoAddress = icaoAddress;
    170. adsbInfo.heading = heading;
    171. adsbInfo.availableFlags = ADSBVehicle::HeadingAvailable;
    172. emit adsbVehicleUpdate(adsbInfo);
    173. } else if (values[1] == QStringLiteral("1")) {
    174. bool icaoOk;
    175. //16进制获取空管地址
    176. uint32_t icaoAddress = values[4].toUInt(&icaoOk, 16);
    177. if (!icaoOk) {
    178. return;
    179. }
    180. ADSBVehicle::VehicleInfo_t adsbInfo;
    181. adsbInfo.icaoAddress = icaoAddress;
    182. adsbInfo.callsign = values[10];
    183. adsbInfo.availableFlags = ADSBVehicle::CallsignAvailable;
    184. emit adsbVehicleUpdate(adsbInfo);
    185. }
    186. }
    187. }

    总结

    这篇文章篇幅较短,主要介绍了ADSB系统,以及ADSB在GGC中是如何处理的,主要模块有三个一个是vehicle是ADSB设备单体,一个是link处理与远程服务器的连接,一个是manger管理所有的vehicle。还有一个setting类因为在setting文件夹下并且继承了setting的某些类所以准备放到讲解setting的时候在讲解。

    下篇我们按照顺序对Aimap文件夹下的代码进行解析。

  • 相关阅读:
    计算一组Tensor的直方图C算法实现
    为“扫清”采用障碍,Oracle 计划将 GraalVM 社区版源代码贡献给 OpenJDK
    基于helm的方式在k8s集群中部署gitlab - 备份恢复(二)
    GBASE 8s 数据库的恢复
    差分数组leetcode 2770 数组的最大美丽值
    设计模式-06-桥接模式
    想进大厂,请先想清楚这几个问题
    fastadmin如何让后台的日期显示成年月日格式
    Git版本管理
    Python案例|Pandas正则表达式
  • 原文地址:https://blog.csdn.net/weixin_43409627/article/details/126950529