Qt 实现 Asterix 报文解析库

文章介绍了作者开发的AsterixParser库,用于解析Cat1到Cat65等多种Asterix类别数据,特别关注Cat21和Cat62,旨在简化航空领域的ADS-B数据解析过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

【写在前面】

        最近工作中需要解析 Cat 21Cat 62 ADS-B 数据 ( 自己的工作包含航空领域 )。

        然后,因为整个 Asterix 协议类别非常之多,每个类别的版本也多,纯手工实现每个版本解析根本不现实 ( 然鹅公司之前的解析库就是这么做的且做的太烂 )。

        于是花了很多时间去寻找一个好用的解析库,比如 Wireshark 的 Asterix 解析部分 ( 可惜因为是插件,有点难移出来 )。

        最后找了很久( Asterix 好像应用比较局限,资料实在太少 ),终于找到一个实现相当良好的工具:AsterixInspector 一个显示Asterix数据文件内容的工具icon-default.png?t=N7T8https://asterix.sourceforge.net/        基于该工具,我将核心部分移植出来并进行简化,最终实现 :Asterix数据报文解析库。icon-default.png?t=N7T8https://github.com/mengps/AsterixParser


【正文开始】

        该库目前支持的类别有:

  - Cat1 (track UAP only)

  - Cat2

  - Cat4

  - Cat7 (downlink UAP only)

  - Cat8

  - Cat10

  - Cat11

  - Cat20

  - Cat21

  - Cat23

  - Cat34

  - Cat48

  - Cat62

  - Cat63

  - Cat64

  - Cat65

  - Cat240

  - Cat247

        因为自己做的工作仅仅是包装得更好用罢了,所以这里简单讲一下用法即可( 偷懒直接用了我的注释 ):

        SimpleAsterixRecordBlock 是报文解析后数据项的存储块。

/**
 * @brief The SimpleAsterixRecordBlock struct
 */
struct SimpleAsterixRecordBlock
{
    /*! [字段引用编号] */
    int frn;
    /*! [数据项ID,例如(I062/070)] */
    QString id;
    /*! [数据项名称] */
    QString name;
    /*! [数据项原始值] */
    QByteArray rawValue;
    /*! [数据项刻度] */
    qreal scale;
    /*! [数据项单位] */
    QString unit;
    /*! [数据项实际值] */
    QVariant value;
    /*! [子数据块列表] */
    QList<SimpleAsterixRecordBlock> subBlock;
};

        SimpleReservedExpansionField 是报文数据项 [RE] 解析后的存储块 ( 目前只支持Cat 21 )。

/**
 * @brief The SimpleReservedExpansionField struct
 */
struct SimpleReservedExpansionField
{
    struct SubField {
        /*! [字段名称] */
        QString name;
        /*! [字段原始值] */
        QByteArray value;
    };
    /*! [字段类型] */
    quint8 type = 0;
    /*! [子字段列表] */
    QList<SubField> subField;
};

        AsterixParser 提供的接口:

    /**
     * @brief getCategory 获取类别
     * @param asterixData Asterix数据包
     * @return int
     */
    int getCategory(const uchar *asterixData);

    /**
     * @brief getU8 字节转U8
     * @param data 原始字节
     * @return quint8
     */
    quint8 getU8(const QByteArray &data);

    /**
     * @brief getU16 字节转U16
     * @param data 原始字节
     * @return quint16
     */
    quint16 getU16(const QByteArray &data);

    /**
     * @brief getU32 字节转U32
     * @param data 原始字节
     * @return quint32
     */
    quint32 getU32(const QByteArray &data);

    /**
     * @brief parseToFsnMap 解析为{fsn, block}映射
     * @param asterixData Asterix数据包
     * @return QMap<int, SimpleAsterixRecordBlock>
     */
    QMap<int, SimpleAsterixRecordBlock> parseToFsnMap(const uchar *asterixData);

    /**
     * @brief parseToIdMap 解析为{id, block}映射
     * @param asterixData Asterix数据包
     * @return QMap<int, SimpleAsterixRecordBlock>
     */
    QMap<QString, SimpleAsterixRecordBlock> parseToIdMap(const uchar *asterixData);

    /**
     * @brief parseReservedExpansionField 解析保留扩展字段
     * @warning 目前仅实现[cat021]
     * @param cat 类别
     * @param ref 扩展字段记录块
     * @return QMap<int, SimpleReservedExpansionField>
     */
    QMap<int, SimpleReservedExpansionField> parseReservedExpansionField(int cat, const SimpleAsterixRecordBlock &ref);

【使用示例】

        使用起来就非常简单了:

#include <QCoreApplication>
#include <QDebug>
#include <QtEndian>

#include "asterixparser.h"

QString applyUnitAndScale(const QVariant &value, qreal scale, const QString &unit)
{
    if (qFuzzyCompare(scale, 1))
        return QString::number(value.toDouble()) + (unit.isEmpty() ? "" : (" "  + unit));
    else
        return QString::number(value.toDouble() * scale, 'f', 10) + (unit.isEmpty() ? "" : (" "  + unit));
}

int main(int argc, char *argv[])
{
    QCoreApplication app(argc, argv);

    //cat021
    uchar test[] = {
          0x15, 0x00, 0x35, 0xcb, 0x19, 0x71
        , 0x11, 0xc1, 0x01, 0x04, 0x16, 0x00, 0x11, 0x44, 0x4c, 0x65, 0x80, 0x09, 0xf1, 0x80, 0x2c, 0x25
        , 0xd8, 0x59, 0xe5, 0xff, 0xe0, 0x07, 0x4c, 0x65, 0x80, 0x02, 0x7b, 0x2d, 0x35, 0x08, 0x12, 0x00
        , 0x03, 0x34, 0x81, 0x37, 0xcf, 0x5d, 0xa0, 0x01, 0x07, 0x88, 0x10, 0x01, 0x11, 0x11, 0x02
    };

    //cat062
    /*uchar test[] = {
        0x3e, 0x00, 0x2b, 0x19, 0x31, 0x10, 0x47, 0x88, 0xf6, 0x00, 0x56, 0xfe, 0x34, 0x01, 0x27, 0xad,
        0x07, 0x00, 0x60, 0x6c, 0x31, 0x00, 0x00, 0x00, 0xc1, 0x01, 0x32, 0xff, 0xe1, 0x01, 0x60, 0x6c,
        0x31, 0x00, 0x00, 0x00, 0x4e, 0xee, 0x00, 0x93, 0x00, 0x00, 0x00
    };*/

    AsterixParser parser(QT_STRINGIFY(PWD_PATH) + QString("/../asterixSpecification"));

    auto map = parser.parseToFsnMap(test);

    for (const auto &block: map) {
        qDebug() << block.frn << block.id << block.name << block.rawValue;
        if (!block.subBlock.isEmpty()) {
            for (const auto &subBlock: block.subBlock)
                qDebug() << "  "
                         << subBlock.frn
                         << subBlock.id
                         << subBlock.name
                         << subBlock.value
                         << applyUnitAndScale(subBlock.value, subBlock.scale, subBlock.unit);
        }
    }

    auto ref_map = parser.parseReservedExpansionField(parser.getCategory(test), map[48]);
    for (const auto &ref: ref_map) {
        for (const auto &subField: ref.subField)
            qDebug() << "  "
                     << subField.name
                     << (subField.value.size() == 1 ? (parser.getU8(subField.value)) : (parser.getU16(subField.value)));
    }


    return app.exec();
}

【效果展示】

        Cat 21 解析结果:

        Cat 62 解析结果:


【结语】

        关于规范文件生成部分[asterixSpecification]:

        一般做法是:[规范pdf] →  [.ast] → [.xml],具体可以看我的项目主页提供的资料。

        项目链接(多多star呀..⭐_⭐):

        优快云 的:

https://download.youkuaiyun.com/download/u011283226/88975636icon-default.png?t=N7T8https://download.youkuaiyun.com/download/u011283226/88975636        Github 的:

https://github.com/mengps/AsterixParsericon-default.png?t=N7T8https://github.com/mengps/AsterixParser

<think>好的,用户想了解如何使用Qt解析和显示ADS-B数据。首先,我需要回忆一下ADS-B的基本知识。ADS-B是广播式自动相关监视系统,飞机通过1090MHz频率发送包含位置、高度、速度等信息的信号。用户提到的引用中有提到S模式应答机、GPS接收机等设备,这些是数据来源。而解析这些数据需要处理二进制报文,比如Asterix格式的Cat 21和Cat 62(引用2提到过)。 接下来,用户想用Qt实现解析和显示。Qt是一个跨平台的C++框架,适合做图形界面。解析部分可能需要处理原始数据,比如从RTL-SDR设备接收的信号(引用3和4提到过RTL-SDR和解析代码)。但直接处理信号可能比较复杂,所以可能需要使用现有的库,比如dump1090或者自己写解析逻辑。不过用户提到希望用C/C++快速实现,所以可能需要整合现有的解析库Qt项目中。 然后,显示部分需要将解析后的数据在地图上实时显示。Qt有地图模块,比如Qt Location,或者集成第三方库如QGIS或WebEngineView加载在线地图(如OpenStreetMap)。同时,数据需要实时更新,可能涉及到多线程处理,防止界面卡顿。 另外,用户的问题中引用了关于Qt链接错误的问题(引用5),所以在开发过程中需要注意库的引用和项目配置,避免出现无法解析的外部符号错误。需要确保.pro文件正确添加了所需的库和头文件路径。 步骤可能包括: 1. 数据接收:使用RTL-SDR设备获取ADS-B信号,通过libusb或RtlSdr库读取数据。 2. 信号解调:将原始信号解调为二进制数据,可能需要实现或使用现有的解调算法。 3. 报文解析:解析Asterix格式的报文,特别是Cat 21和Cat 62,提取飞行数据。 4. Qt界面开发:创建窗口应用程序,集成地图显示,实时更新飞机位置等信息。 5. 数据可视化:在地图上用图标标记飞机,显示航班号、高度、速度等详细信息。 6. 处理多线程:确保数据接收和解析在后台线程运行,避免阻塞UI线程。 可能遇到的问题包括信号解调的准确性、报文解析的正确性、Qt地图组件的性能,以及跨平台的兼容性。需要参考现有的开源项目如dump1090或ADSBsharp(引用3和4提到)的实现方式,结合Qt的特性进行开发。 此外,用户提供的引用中有提到ADSBsharp程序的解析部分,可以参考其数据处理的流程,将C#代码转换为C++/Qt实现。同时,注意项目配置,正确添加依赖库,避免链接错误(引用5的问题)。 总结起来,步骤大致是硬件数据接收、信号处理、数据解析、Qt界面设计与数据展示,每个环节都需要结合Qt的库和现有的ADS-B处理逻辑,确保高效和实时性。</think>### 使用Qt解析和显示ADS-B数据的实现步骤 #### 1. 数据接收与硬件配置 - **硬件需求**:使用RTL-SDR接收器捕获1090MHz的ADS-B信号,需安装驱动(如`librtlsdr`)并配置Qt项目引用相关库[^4]。 - **代码示例**(数据接收线程): ```cpp #include <rtl-sdr.h> class AdsbReceiver : public QThread { Q_OBJECT protected: void run() override { rtlsdr_dev_t *dev; rtlsdr_open(&dev, 0); rtlsdr_set_sample_rate(dev, 2e6); rtlsdr_set_center_freq(dev, 1090e6); // 持续读取数据并发送信号 emit dataReceived(buffer); } }; ``` #### 2. 信号解调与报文解析 - **解调逻辑**:ADS-B信号为脉码调制(PPM),通过幅度阈值检测解调为二进制流[^4]。 - **Asterix报文解析**: - 使用Qt实现Cat 21/Cat 62报文解析库,参考开源项目如`libasterix`[^2]。 - 关键字段提取示例: ```cpp struct Cat21Record { QString callsign; double latitude; double longitude; int altitude; }; Cat21Record parseCat21(const QByteArray &data) { // 解析二进制数据并填充结构体 } ``` #### 3. Qt界面设计与地图集成 - **地图组件选择**: - **方案1**:使用`QWebEngineView`加载在线地图(如OpenStreetMap)。 - **方案2**:集成第三方库(如QGIS或自定义QGraphicsScene绘制)。 - **动态飞机标记**: ```cpp // 在地图坐标上绘制飞机图标 void MapWidget::updateAircraftPosition(const Cat21Record &record) { QPointF pos = convertCoordToPixel(record.latitude, record.longitude); aircraftIcons[record.callsign]->setPos(pos); } ``` #### 4. 多线程与性能优化 - **数据流分离**: - 接收线程(`AdsbReceiver`) -> 解析线程(`AdsbParser`) -> UI更新信号(通过`QMetaObject::invokeMethod`跨线程安全更新)[^3]。 - **Qt信号槽连接**: ```cpp connect(parser, &AdsbParser::newAircraftData, mapWidget, &MapWidget::updateDisplay); ``` #### 5. 常见问题解决 - **库链接错误**:在`.pro`文件中添加库路径和依赖项: ```qmake LIBS += -lrtlsdr -lasterix INCLUDEPATH += /usr/include/librtlsdr ``` 若出现`LNK2001`错误,需检查动态库文件是否存在[^5]。 #### 6. 完整示例框架 ```cpp // main.cpp int main(int argc, char *argv[]) { QApplication app(argc, argv); MapWidget map; AdsbReceiver receiver; AdsbParser parser; QObject::connect(&receiver, &AdsbReceiver::dataReceived, &parser, &AdsbParser::processRawData); QObject::connect(&parser, &AdsbParser::newAircraftData, &map, &MapWidget::updateAircraft); receiver.start(); map.show(); return app.exec(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦起丶

您的鼓励和支持是我创作最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值