机器人地面站-[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对应,属于被管理者。

 首先分析头文件:

/****************************************************************************
 *
 * (c) 2009-2020 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 <QObject>
#include <QGeoCoordinate>
#include <QElapsedTimer>

#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<MyStruct>();

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

cc文件如下

/****************************************************************************
 *
 * (c) 2009-2020 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 "ADSBVehicle.h"
#include "QGCLoggingCategory.h"
#include "QGC.h"

#include <QDebug>
#include <QtMath>

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);
}

二.ADSBVehicleManager

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

/****************************************************************************
 *
 * (c) 2009-2020 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 "QGCToolbox.h"
#include "QmlObjectListModel.h"
#include "ADSBVehicle.h"

#include <QThread>
#include <QTcpSocket>
#include <QTimer>
#include <QGeoCoordinate>

//定义类
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 <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 "ADSBVehicleManager.h"
#include "QGCLoggingCategory.h"
#include "QGCApplication.h"
#include "SettingsManager.h"
#include "ADSBVehicleManagerSettings.h"

#include <QDebug>

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<ADSBVehicle*>(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<quint16>(_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文件夹下的代码进行解析。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值