【写在前面】
最近工作中需要解析 Cat 21 和 Cat 62 的 ADS-B 数据 ( 自己的工作包含航空领域 )。
然后,因为整个 Asterix 协议类别非常之多,每个类别的版本也多,纯手工实现每个版本解析根本不现实 ( 然鹅公司之前的解析库就是这么做的且做的太烂 )。
于是花了很多时间去寻找一个好用的解析库,比如 Wireshark 的 Asterix 解析部分 ( 可惜因为是插件,有点难移出来 )。
最后找了很久( Asterix 好像应用比较局限,资料实在太少 ),终于找到一个实现相当良好的工具:AsterixInspector 一个显示Asterix数据文件内容的工具https://asterix.sourceforge.net/ 基于该工具,我将核心部分移植出来并进行简化,最终实现 :Asterix数据报文解析库。https://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));elsereturn QString::number(value.toDouble() * scale, 'f', 10) + (unit.isEmpty() ? "" : (" " + unit));
}int main(int argc, char *argv[])
{QCoreApplication app(argc, argv);//cat021uchar 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呀..⭐_⭐):
CSDN 的:
https://download.csdn.net/download/u011283226/88975636https://download.csdn.net/download/u011283226/88975636 Github 的:
https://github.com/mengps/AsterixParserhttps://github.com/mengps/AsterixParser