尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

基于CSN.1的GMR-1/GMR-3G卫星信令编解码与抓包解析

基于CSN.1的GMR-1/GMR-3G卫星信令编解码与抓包解析 简介面向移动卫星通信协议开发与地面测试人员的专业pdf文档聚焦卫星通信信令的编解码实现。内容围绕CSN.1协议处理器设计展开梳理其与ASN.1在抽象数据结构与比特级操作上的差异针对GMR-1、GMR-3G终端系统中CSN.1编解码器定义及链路层控制消息解析工具缺失的现状给出可扩展软件系统的实现思路以CSN描述文件与消息函数库为核心通过M-操作函数完成编解码兼容gcc、arm-linux-gcc、armcc等编译工具传输采用tgmrp-msg封装支持网口与USB消息捕获。资源为单个pdf文件压缩包约601KB篇幅精炼便于通读查阅目前已有82人学习。读者可借此了解协议处理器组成原理、分层解码流程与通用接口设计获得可移植架构参考适合卫星通信协议栈开发、嵌入式移植与测试工程师研读。1. 从一条抓包记录说起GMR-3G 信令为什么难解析抓包工具里出现这样一条记录TEST-GMRP Header, ARFCN:0, Ts:0, Channel:DCH_8往下展开是GMR-1 RRC (Uplink)再往下是Message_Type: INITIAL DIRECT TRANSFER最后挂着一串 NAS 消息CM Service Request (0x24)里面还能看到TMSI/P-TMSI、Ciphering Key Sequence Number和Integrity Check。做过地面通信协议解析的人对这种嵌套结构应该不陌生但换成卫星链路很多人第一反应是「这玩意儿用什么工具解」。问题的根子在语法层。地面蜂窝网络那一套习惯用 ASN.1 描述数据结构先把抽象的数据类型定死再靠编译器生成编解码代码边界清楚、上手快但遇到 GMR-1、GMR-3G 这类链路层协议里大量比特位级操作时ASN.1 的「死板」就暴露了字段可能是 1 bit 的对齐填充可能是根据前一个 bit 决定是否存在的可选段也可能是左对齐可变位图这些在抽象数据类型体系里表达起来很别扭。CSN.1 走的是另一条路。它直接定义编码流里比特位怎么排哪些是定长、哪些跟着前面的值走、哪些可以递归展开表达能力天然贴合链路层信令。这份「移动卫星通信系统协议处理器设计与应用」给的方案本质就是拿 CSN.1 作为统一语法规则配一份 CSN-DESCR 描述文件和一套消息函数库把 RRC、RLC/MAC 的比特流双向翻译成 C 结构体再塞进抓包分析软件里看。它面向的是卫星地面测试人员和终端开发人员——手上有一堆 GMR-1、GMR-3G 信令要解、要造、要验证但缺一套趁手的编解码工具。2. CSN.1 语法关键点与 CSN-DESCR 描述文件设计2.1 CSN.1 与 ASN.1 的本质差别先把选型说清楚不然后面所有设计都像是凭空冒出来的。ASN.1 描述的是一个抽象的数据结构比如「这是一个 SEQUENCE里面有一个 INTEGER 叫做 id」编解码器负责把抽象结构映射成字节流映射规则BER、PER是另外规定的。CSN.1 不这样它描述的就是编码流本身第几位是什么含义、某个字段占几个 bit、下一个字段存不存在取决于什么条件。差别落到工程上就两点。第一CSN.1 对「条件字段」「可变位图」「递归数组」这类结构有原生支持GMR 协议里这类结构很多。第二CSN.1 允许有限度地容忍不规范输入解码时不至于碰到一个字段不符合预期就整条消息报废。这两点在协议已经冻结、而实测比特流经常有厂商私有位、对齐填充差异的场景里非常关键。2.2 CSN-DESCR 字段结构CSN-DESCR 是编解码系统能识别的数据结构每个字段描述一条消息的语法。设计上它是一个描述元素数组每个元素对应一个编解码操作。基本元素的命名规律是CSN_XXX比如CSN_END表示一条描述结束CSN_BIT取一位CSN_UINT取定长无符号整数CSN_TYPE嵌套另一个类型CSN_CHOICE做分支选择CSN_UNION做联合体CSN_FIXED取固定值CSN_CALLBACK回调到用户代码处理特殊字段。带_LH后缀的如CSN_UNION_LH、CSN_EXIST_LH表示长度头先读长度再决定后续还要读多少。下面是描述一个简单计数器消息的示意实际项目里的描述文件比这长得多但结构完全一致。/* 一条 GMR 计数类消息的 CSN-DESCR 描述片段示意 */ static const CSN_DESCR_DEF CounterMsg_Descr[] { { CSN_UINT, 8, (void*)offsetof(CounterMsg, msgType) }, { CSN_UINT, 1, (void*)offsetof(CounterMsg, moreFlag) }, { CSN_EXIST, 0, (void*)offsetof(CounterMsg, extField) }, /* 由 moreFlag 决定是否存在 */ { CSN_UINT, 16, (void*)offsetof(CounterMsg, extValue) }, { CSN_NULL, 0, NULL }, /* 占位保持对齐语义 */ { CSN_END, 0, NULL } };逻辑说明offsetof把每个描述元素绑到 C 结构体的具体成员上解码时逐个元素读比特流并写回结构体编码时反过来。CSN_EXIST这一项本身不消费比特它根据前一步读出的moreFlag决定后面的extValue是否参与。参数说明第二个字段是位宽或标志位第三个字段是目标偏移CSN_END必须放在末尾——少了它解码器会一直往下读这是最常见的段错误来源之一。2.3 扩展描述覆盖卫星链路层基础 CSN 描述解决不了卫星协议的全部需求所以要封装一批扩展描述。常用的几组扩展描述用途卫星链路层典型场景CSN_UINT_OFFSET读一个偏移量变长字段的起始定位CSN_SERIALIZE序列化嵌套结构RRC 直传单元内嵌 NASCSN_BITMAP定长位图承载类型、能力位CSN_VARIABLE_BITMAP / _1可变位图时隙与信道组合标识CSN_LEFT_ALIGNED_VAR_BMP_8/16左对齐可变位图厂商私有位段CSN_PADDING_BITS显式跳过填充位字节对齐收尾CSN_VARIABLE_ARRAY变长数组邻区列表、测量报告CSN_RECURSIVE_ARRAY递归数组树形信元列表CSN_UINT_ARRAY定长类型数组固定长度参数组CSN_VARIABLE_ARRAY、CSN_RECURSIVE_ARRAY这类必须配合长度头使用否则数组边界无从判断。设计里把左对齐可变位图按 8 位和 16 位各封一套是因为 GMR-1 和 GMR-3G 在不同信道上对位段对齐的处理不一样实测时同一消息可能按其中一种解析才正确。提示写描述文件时先用抓包工具找几条已经确认正确的消息对照着写每加一个字段就回放一遍。描述文件里字段顺序错一位后面全部错位但解码器不会报错只会静默给出错误值这种错最难查。3. 编解码流程实现从比特流到 C 结构体的双向转换3.1 软核分层与接口划分处理器软核按功能分层最底层是公共支持库提供那批CSN_XXX描述元素中间层是消息函数库把描述元素组装成具体消息的 M-操作函数上层是接口层对外暴露编解码入口。分层的好处是加协议或者加消息只动中间层底层公共库和上层接口不动。消息函数库里与编码相关的 M-操作函数包括csnStreamInit()、csnStreamEncoder()、ProcessError()与解码相关的包括csnStreamInit()、csnStreamDecoder()、ProcessError()、ExistNextElement()。编解码共用初始化函数但走不同主干这也是为什么 RRC 层和 RLC/MAC 层之间不需要互相调用对方的 M-操作函数——两边各自面向自己的信令集合只在编解码器这里交汇。3.2 解码步骤与调用关系解码的入口是csnStreamDecoder()。调用前先csnStreamInit()把解码上下文初始化把收到的字节流转成 bit vector这一步要处理位序GMR 消息里上下行位序不完全一致。然后按序号走M-CHOICE自动匹配分层、上下行、承载类型这些维度挑出该用哪个消息描述。匹配到之后从消息函数库里取对应的 CSN-DESCR 和 M-操作函数逐元素解出字段写进 C 语言结构体。/* 解码主干从 RRC / RLC-MAC 层收到比特流后的处理骨架 */ int decode_gmr_msg(const uint8_t *rawBuf, size_t rawLen, GmrMsg_t *outMsg, GmrDecodeCtx_t *ctx) { int rc; rc csnStreamInit(ctx-stream, rawBuf, rawLen, CSN_DIR_DECODE); if (rc ! 0) { ProcessError(ctx, rc, __LINE__); return rc; /* 上下文初始化失败直接返回 */ } /* 按 CHOICE 序号自动匹配分层 / 上下行 / 承载类型 */ rc csnStreamDecoder(ctx-stream, GmrChoiceDescr, outMsg); if (rc ! 0) { ProcessError(ctx, rc, __LINE__); /* 描述不匹配或位流越界 */ return rc; } /* 处理可选段的递归存在性判断 */ while (ExistNextElement(ctx)) { rc csnStreamDecoder(ctx-stream, ctx-nextDescr, outMsg); if (rc ! 0) { ProcessError(ctx, rc, __LINE__); break; } } return rc; }逻辑说明csnStreamInit负责把裸字节流和方向信息装进上下文csnStreamDecoder是主解码循环靠描述文件驱动逐字段消费比特位ExistNextElement处理那些「根据前一个元素决定后面还有没有元素」的结构比如可选扩展位图循环到没有下一个可存在元素为止。参数说明rawBuf指向裸比特流rawLen是字节长度outMsg是解析结果结构体ctx是解码上下文里面保有当前比特位置和方向标志。ProcessError的第三个参数是行号方便定位是哪个位置出的错。注意解码器返回非 0 不一定代表整条消息不可用也可能是某个可选段没解析成功。工程上常见做法是记录错误位置后继续尝试后续段最后看整体解析率而不是一遇错就放弃整条消息。3.3 编码路径与 public library 复用编码方向和上面基本镜像。业务层先填好 C 结构体调用csnStreamEncoder()它根据同一份 CSN-DESCR 逐字段把结构体转回 CSN 结构再压成比特流返回给 RRC 或 RLC/MAC 层。csnStreamInit在编码路径上初始化的是输出上下文ProcessError复用同一套错误处理。解码和编码共用公共支持库是这套设计省事的关键。同一套CSN_FIXED、CSN_PADDING_BITS描述在两条路径上语义一致不会出现「解码认、编码不认」的偏差。移植到别的平台时改的是编译工具链适配层描述文件和消息函数库基本原样搬过去支持 gcc、arm-linux-gcc、armcc 就是这么来的。3.4 有限度压缩与回送编解码器收到 RRC 或 RLC/MAC 层消息后先按 CSN 语法解码成可识别结构做参数识别再对信令做有限度压缩然后重新编码成压缩后的比特流发回原层。压缩不是重新设计协议而是把重复出现的信元按已解析结果做等价表达减少回送数据量。回送的目标层和来源层相同不做跨层回送这样每一层拿回自己的消息格式业务逻辑不用改。4. 移植进抓包工具TEST-TGMRP 封装与字段解析4.1 tgmrp-msg 报头设计抓包工具的解析库默认是给地面通信协议用的直接套上去卫星消息会认不出来所以要有自己的封装。传输采用tgmrp-msg封装解析库里用TEST-TGMRP命名。报头字段和对应的解析名称如下报头字段含义抓包工具中的解析名Payload Type承载类型GMR-1 air interface (MES-LS-GTS)Time Slot时隙Time SlotARFCN频点ARFCNDirection上下行Uplink / DownlinkSN-I/Noise Ratio信噪比Sig-I/Noise Ratio (dB)Signal Level收发电平Signal Level (dBm)GSM Frame Number卫星大系统帧号GSM Frame NumberChannel Type卫星信道Channel Type: DCH_8 (34)Terminal ID终端 IDMES-IDGSM Frame Number这个命名是历史遗留——抓包工具原本是为地面通信协议写的字段名沿用了旧模板实际承载的是卫星大系统帧号名字有歧义但不影响解析正确性。4.2 移植步骤与过滤项移植的具体动作常见做法是这几步把编译好的编解码库按抓包工具的插件接口格式打包导出解析入口和字段展示回调。在解析库注册新协议名加入GMR RRC、GMR RLC两个过滤项。注册TEST-TGMRP报头解析器把上表字段按偏移绑定到报头结构。数据区挂上 RRC 消息解析器RRC 直传单元里再挂 NAS 解析器。编译加载用已知正确的报文回放验证字段显示是否与预期一致。过滤项只加GMR RRC、GMR RLC两个覆盖接入层NAS 消息通过 RRC 直传单元被间接解析不再单独挂一个接入点因为 NAS 消息本身不直接出现在链路上它封装在 RRC 消息里。# 本地编译带解析库的抓包工具示意按实际工具链调整 gcc -c gmr_csn_decoder.c -o gmr_csn_decoder.o \ -DGMR_TARGET_LINUX \ -I./csn_public_lib/include gcc -shared -o gmr_parser.so gmr_csn_decoder.o \ ./csn_public_lib/libcsn_public.a # 交叉编译到嵌入式侧时换工具链 arm-linux-gcc -c gmr_csn_decoder.c -o gmr_csn_decoder_arm.o \ -DGMR_TARGET_ARM_LINUX逻辑说明先编译解码器目标文件-DGMR_TARGET_LINUX用来切换平台相关代码再链接成动态库供抓包工具加载。交叉编译那一步换arm-linux-gcc宏换成 ARM 平台对应的公共库需要重新用同一工具链编译过否则链接期会报符号不兼容。参数说明-I指公共库头文件路径-shared生成动态库最后那个.a是公共支持库的静态库。armcc 工具链下产物格式不同一般交给 IDE 管理不手写命令行。4.3 完整消息解析效果回看开头那条记录解析链条是这样的TEST-GMRP Header给出频点、时隙、信道DCH_8 (34)、终端MES-IDGMR-1 RRC (Uplink)给出Message_Type: INITIAL DIRECT TRANSFER直传单元里解出Protocol Discriminator: Mobility Management messages、DTAP Mobility Management Message Type: CM Service Request (0x24)再往下是TMSI/P-TMSI (0x1a2d50a8)、Ciphering Key Sequence Number、CM Service Type、Mobile Identity、Extra neous Data、Integrity Check_Info最后数据长度8字节 192 位。这条链上一环扣一环任何一环描述写错后面的字段全部错位。实际调试时最省事的做法是拿一条已知正确的消息从报头开始逐段关掉后面的解析器确认当前段字段对得上再往下开比整条一起调快得多。5. 定位错位字段与描述文件回放技巧5.1 位图与对齐问题的排查错位字段里最难查的是可变位图和左对齐位段。典型症状是前面几个字段都对从某个位图开始整体偏移一两位。原因通常是协议里是 8 位左对齐描述文件按 16 位读了或者反过来。处理办法是先确认这条消息所用的信道类型不同信道对齐规则不同。/* 用 CALLBACK 打印实际消费的比特位置定位错位起点 */ static int trace_bitpos_cb(void *ctx, void *data) { GmrDecodeCtx_t *c (GmrDecodeCtx_t *)ctx; fprintf(stderr, [trace] element%s bitpos%zu remain%zu\n, (char *)data, c-stream.bitPos, c-stream.totalBits - c-stream.bitPos); return 0; /* 返回 0 表示不改变解析流程 */ }逻辑说明把trace_bitpos_cb挂到怀疑出错的描述元素上每解完一个元素打印当前比特位置和剩余位数。前面对、后面开始错的那一行就是错位起点。参数说明返回值必须是 0非 0 会让编解码主循环以为回调改变了控制流。bitPos是已消费位数totalBits是总位数两者相减看剩余。5.2 自造消息验证编码路径解码能对上不代表编码就正确两条路径要各自验证。最直接的办法是用已知结构体编码出一条消息再用同一个解码器解回来对比两端字段是否一致。这个往返测试round-trip能覆盖大部分描述文件的不对称错误——比如某个字段解码时读了、编码时没写。几个验证要点边界值优先1 bit 位图取 0 和 1 各测一次16 位无符号整数取 0、最大值、中间值。可选段必测把决定可选段存在与否的那个字段翻一遍确认编码后长度随预期变化。填充位必测CSN_PADDING_BITS数量算错整条消息长度是对的但末尾字节内容不对抓包工具只看长度看不出来。递归数组深度测两级以上只测一级看不出递归边界处理是否正确。提示往返测试通过后再拿真实抓包数据过一遍。自造消息太干净覆盖不到厂商私有位和实测对齐差异这两类问题只有真实数据能暴露。5.3 提高解析速度的几个参数点解析性能上公共支持库是复用关键但真正影响速度的是这几处。第一bit vector 的读写用位运算批量取而不是一位一位挪长消息差异很明显。第二消息描述查找用映射表而不是线性扫消息类型一多线性扫的代价就上来了。第三递归数组的栈使用尽量压低深递归在嵌入式侧容易栈溢出这也是为什么描述里有_ARRAY_1、_ARRAY_2这种分级封装给调用方按深度选。移植到不同平台时位序和字节序要重点核对。x86 和 ARM 在字节序一致的情况下位序处理差异往往出在描述文件里的左对齐约定上同一份描述换平台结果不同基本可以锁定在这。本文还有配套的精品资源点击获取
返回列表