参考文档《采样包结构与实时数据传输封包逻辑设计.pdf》,相关组包格式设计已在该文档中完整写入。
在解析过程中,上位机端需要根据配置参数知晓,各个子卡配置的原始采样率、降采样倍率,计算得到有效采样率 = 原始采样率 / 降采样倍率,之后根据各源有效采样率计算得到单个采样包的时间窗口长度和时隙宽度,从而得出单个采样包里面的时隙数量,并结合各个子卡采集数据的有效采样率,得到每个时隙里面子卡采样点之间的排布规则。
之后,再通过子卡配置的通道数量和通道掩码,得出各个子卡采样点的大小,以及对应子卡的选定通道在采样包里面各个时隙中所在偏移地址。
例如以下配置:
| 子卡编号 | 子卡有效采样率 | 子卡开启通道 |
|---|---|---|
| 子卡0 | 2 MSPS | 通道0~31,共32通道 |
| 子卡1 | 2 MSPS | 通道0~31,共32通道 |
| 子卡2 | 40 MSPS | 通道0~3,共4通道 |
| 子卡3 | 80 MSPS | 通道0~2,共3通道 |
| 子卡4 | 80 MSPS | 通道0~2,共3通道 |
可得:
采样包窗口:500ns
基本时隙:12.5ns
每包时隙数:40
各卡包内点数:1、1、20、40、40
本例假设各子卡不进行降采样,列出的通道全部启用,且各子卡使用共同的采样时间基准,在采样包起点对齐。进而解析得到以下排布。
时隙编号从0开始;“时隙偏移时间”表示该时隙起点相对于当前采样包起点的时间偏移;“时隙偏移字节数”表示该时隙数据相对于当前采样包数据区起点的起始字节偏移,从0开始,不包含任何包头。本例每通道数据为16位(2字节)。子卡i[j]表示子卡i在当前采样包内的第j个子卡采样点,j从0开始;每个子卡采样点包含该子卡本次采样的全部启用通道数据。
“时隙数据宽度”表示该时隙内全部子卡采样点实际占用的字节数,不表示时间长度;本例每个时隙的时间长度均为12.5 ns。
| 时隙编号 | 时隙偏移时间 | 时隙偏移字节数 | 时隙内子卡采样点排布 | 时隙数据宽度(字节) |
|---|---|---|---|---|
| 0 | 0 ns | 0 | 子卡0[0] → 子卡1[0] → 子卡2[0] → 子卡3[0] → 子卡4[0] | 148 |
| 1 | 12.5 ns | 148 | 子卡3[1] → 子卡4[1] | 12 |
| 2 | 25 ns | 160 | 子卡2[1] → 子卡3[2] → 子卡4[2] | 20 |
| 3 | 37.5 ns | 180 | 子卡3[3] → 子卡4[3] | 12 |
| 4 | 50 ns | 192 | 子卡2[2] → 子卡3[4] → 子卡4[4] | 20 |
| ··· | ··· | ··· | ··· | ··· |
| 36 | 450 ns | 704 | 子卡2[18] → 子卡3[36] → 子卡4[36] | 20 |
| 37 | 462.5 ns | 724 | 子卡3[37] → 子卡4[37] | 12 |
| 38 | 475 ns | 736 | 子卡2[19] → 子卡3[38] → 子卡4[38] | 20 |
| 39 | 487.5 ns | 756 | 子卡3[39] → 子卡4[39] | 12 |
表格按从上到下、每行从左到右的顺序串行排列数据。子卡0、1的每个采样点内部按通道0~31排列,子卡2按通道0~3排列,子卡3、4按通道0~2排列。不同子卡采样点以及相邻时隙的数据紧密连接,不插入补零或对齐字节;未参与某个时隙的子卡不占用数据空间。
通过该规则,可以知晓各个时隙在采样包中的偏移字节数,结合时隙内的子卡采样点排布规则,得出所选的通道在各个时隙内的偏移地址,最终通过该方式,上位机端解析出所选子卡对应通道的采集数据。
定义:
F:一个完整采样包的数据字节数
C:一个UDP包允许承载的采集数据字节数 = UDP最大payload长度 − 应用层包头长度
在完成参数配置后,生产者程序读取当前配置的相关参数,根据相关配置信息计算:
并根据配置信息得出接收UDP包工作模式:
另外,包头需要至少表达以下相关信息:
| 信息 | 用途 |
|---|---|
| 首个采样包编号 | 分片模式下表示所属采样包 |
| 完整采样包数量 | 完整模式下表示当前UDP包含几个采样包 |
| 分片序号、分片总数 | 确定分片位置及是否收齐 |
| 数据区有效长度 | 确定本包实际携带多少采集数据 |
生产者程序需要在不同模式下,根据上述信息分析接受逻辑。
该模式的工作逻辑和初版的上位机接收采集数据UDP包的逻辑基本一致。具体可以分为以下相关步骤:
例如,还是按照前面,子卡0,1配置2M采样率32通道,子卡2配置40M采样率4通道,子卡3配置80M采样率3通道,则Producer执行:
计算得到当前配置下,单个采样包大小为768字节
↓
结合UDP包最大容量分析可知,单个UDP包携带两个完整采样包
↓
解析包头,得到首编号100、数量2
↓
确认数据区足够容纳2 × 768字节
↓
复制采样包100到共享内存,发布对应ring项
↓
复制采样包101到共享内存,发布对应ring项
↓
继续接收下一个UDP
分片模式下,可以在“接收UDP”和“写入共享内存”环节之间,增加采样包重组环节;采样包一旦收齐,就将该完整采样包写入并发布。通过该方式,保证共享内存里面依然由完整采样包组成,而不需要知晓一个完整采样包经过了多少个UDP分片。
在生产者程序启动工作后,先根据生效配置得到:
采样包长度:F
UDP包数据区容量:C
分片数量:向上取整(F / C)
例如,假设某采样包长度F=6600,UDP包数据区容量C=2000,则:
| 分片序号 | 采样包内字节偏移 | UDP包数据区长度 |
|---|---|---|
| 0 | 0 | 2000 |
| 1 | 2000 | 2000 |
| 2 | 4000 | 2000 |
| 3 | 6000 | 600 |
之后每收到一个UDP包,需要先明确,这个UDP包属于哪一个采样包,具体位于该采样包的哪个部分。因此,包头需要明确知晓:
采集标识、配置标识
采样包编号
分片序号
本片有效数据长度
收到数据后,Producer根据身份查找重组槽位:
例如依次收到采样包100的分片:
| 到达分片 | 处理动作 | 已收到分片集合 | 是否发布 |
|---|---|---|---|
| 0 | 写入字节偏移0 | {0} | 否 |
| 2 | 写入字节偏移4000 | {0, 2} | 否 |
| 2(重复) | 忽略,不重复计数 | {0, 2} | 否 |
| 1 | 写入字节偏移2000 | {0, 1, 2} | 否 |
| 3 | 写入字节偏移6000 | {0, 1, 2, 3} | 是 |
当完成整个采样包完整接收,重组缓存内已经是完整、正确排列的采样包后,将才采样包写入共享内存中的一个采样包槽位,然后发布一个ring条目,之后释放重组槽位继续接受后续采样包。
一次采样包的所有分片收齐,就可以发布,不必等待同一个Blob的其他采样包。
本文章使用limfx的vscode插件快速发布