简介:面向气象数据处理、雷达产品开发与教学科研人员的CINRAD雷达基数据格式参考文档,聚焦SA/SB与CA/CB两类雷达的二进制存储规范,帮助读者解决基数据解析、字段定位与解码换算中的实际问题。资源包共1个doc文件,约92KB,以字节顺序表形式逐项列出雷达信息头、径向数据收集时间、儒略日、不模糊距离、方位角与仰角编码、径向数据状态、体扫仰角数、反射率与多普勒数据的首库距离、库长、库数及数据指针等字段,并给出反射率、速度、谱宽的编码公式与无回波、距离模糊的取值约定。文档还说明每个体扫单独存为一个文件、径向数据按仰角由低到高排列、径向长度固定为2432字节,以及SA/SB与CA/CB在库长和最大库数上的差异。已有2806人学习,适合需要对照字节表编写解析程序或核对数据结构的读者查阅。
1. 雷达基数据格式:一份被二进制藏起来的天气日记
如果你打开过一个雷达基数据文件,第一反应大概率是——这玩意儿怎么全是乱码?没有表头、没有逗号分隔、没有 JSON 的花括号,用文本编辑器打开就是一屏看不懂的字符。可它偏偏记录着一整场天气过程的全部细节:哪个方向、哪个高度、多远距离上,回波有多强、风在往哪吹、气流有多乱。
通俗地说,雷达基数据格式就是一套“约定”:雷达把探测结果按固定顺序、固定字节长度写进文件,读的人必须拿着同一份约定,一个字节一个字节地对号入座,才能把二进制还原成反射率、速度、谱宽这些我们能看懂的量。它像一本没有目录的账本,页码是死的,第几页记什么完全靠规矩。搞懂这套格式,你才能把雷达的原始观测变成图、变成数据、变成能判断天气的东西;搞不懂,文件就永远是个黑匣子。这一篇就围绕 CINRAD SA/SB 与 CA/CB 这两类雷达的基数据格式,把“它是什么、为什么这么设计、容易混在哪”讲清楚。
2. 为什么雷达数据要按字节死磕:固定长度背后的取舍
2.1 一个体扫一个文件,一条径向固定 2432 或 4132 字节
先说存储方式。每个体扫(一次完整的立体扫描)存成一个独立文件,文件内部按径向数据一条一条顺序排列,从低仰角开始,一路排到高仰角结束。所谓径向,就是雷达天线在某个方位角上“扫”出去的那一条射线,这条射线上每隔一段距离取一个采样点,这些点叫距离库。
关键在于:每条径向的长度是固定的。SA/SB 雷达一条径向固定 2432 字节,CA/CB 雷达固定 4132 字节。固定长度意味着什么?意味着你不需要任何分隔符,只要知道第几条径向,就能用“起始字节 + 序号 × 长度”直接跳到它。打个比方,这就像一栋每层户型完全一样的楼,找 302 房间不用逐户敲门,算一下楼梯层数就行。代价是灵活性——不管这条径向上实际有多少有效数据,空间都得按最大值预留,浪费是必然的,但换来的是读取时的确定性和速度。
2.2 28 字节信息头:整条径向的“身份证”
每条径向开头都有 28 字节的雷达信息头,里面塞满了描述这条径向的元信息。别小看这 28 字节,它是解读后面所有数据的钥匙。举几个最常用的字段:
| 字节位置 | 含义 | 编码方式 |
|---|---|---|
| 15-16 | 数据类型标识 | 1 表示雷达数据 |
| 29-32 | 径向收集时间 | 毫秒,自 00:00 起算 |
| 33-34 | 儒略日 | 自 1970 年 1 月 1 日起算 |
| 35-36 | 不模糊距离 | 数值 / 10 = 千米 |
| 37-38 | 方位角 | [数值/8] × [180/4096] = 度 |
| 41-42 | 径向数据状态 | 0 首条、1 中间、2 末条、3 体扫首条、4 体扫末条 |
| 43-44 | 仰角 | [数值/8] × [180/4096] = 度 |
| 45-46 | 体扫内仰角数 | 直接读数 |
| 65-66 | 反射率数据指针 | 偏离信息头的字节数 |
| 71-72 | 多普勒速度分辨率 | 2 表示 0.5 米/秒,4 表示 1.0 米/秒 |
| 73-74 | 体扫模式 VCP | 11/21 降水,31/32 晴空 |
| 89-90 | Nyquist 速度 | 数值 / 100 = 米/秒 |
这里最容易被忽略的是“指针”字段。反射率、速度、谱宽三块数据在径向里的起始位置不是写死的,而是由 65-66、67-68、69-70 三个双字节指针告诉你“从信息头往后数多少字节开始”。为什么要这么设计?因为不同雷达、不同模式、不同分辨率下,三块数据的长度会变,用指针比用固定偏移更稳。读的时候先读指针,再按指针跳过去,这是正确姿势。
2.3 编码不是直读:数值要经过一次“翻译”
原始字节里存的不是物理量本身,而是编码后的整数。以反射率为例,SA/SB 雷达的编码方式是(数值 - 2) / 2 - 32 = dBZ。也就是说,你读到一个字节值 66,先减 2 得 64,除以 2 得 32,再减 32 得 0 dBZ。速度的编码分两种:分辨率 0.5 米/秒时是(数值 - 2) / 2 - 63.5,分辨率 1.0 米/秒时是(数值 - 2) - 127。谱宽则统一按(数值 - 2) / 2 - 63.5处理。
还有两个特殊值必须记住:数值为 0 表示无回波(低于信噪比阈值),数值为 1 表示距离模糊。这两个值不能套公式算,得单独判断,否则会算出毫无意义的物理量。很多初学者第一次读数据发现图上一片诡异的负值,八成就是忘了处理这两个哨兵值。
2.4 为什么库长和库数要分波段定死
反射率和多普勒(速度、谱宽)用的距离库长不一样,这是设计上的必然。SA/SB 雷达反射率库长 1000 米、最大 460 个库,速度和谱宽库长 250 米、最大 920 个库;CA/CB 雷达反射率库长 500 米、最大 800 个库,速度和谱宽库长 125 米、最大 1600 个库。反射率要看得远,库长就粗;速度和谱宽要看得细,库长就短、库数就多。这就像用望远镜看远景和用放大镜看近物,目的不同,刻度自然不同。读数据前先确认自己手上是哪个波段,库数设错了,整张图会错位。
3. 两类雷达格式的区别:SA/SB 与 CA/CB 到底差在哪
3.1 径向长度和库数:2432 对 4132 的连锁反应
最直观的差别是径向长度:SA/SB 是 2432 字节,CA/CB 是 4132 字节。这不是随便定的,而是由库数决定的。SA/SB 反射率最多 460 个库、速度谱宽最多 920 个库,加起来约 2300 字节的数据区,加上信息头和保留区凑成 2432;CA/CB 反射率 800 库、速度谱宽各 1600 库,数据区约 4000 字节,凑成 4132。所以看到文件里每条径向的长度,基本就能反推是哪类雷达。
这个差别会连锁影响到读取程序。原文给出的数组定义里,RGates、VGates、WGates三个常量就是按波段切换的开关:读 S 波段时RGates = 460、VGates = 920、WGates = 920;读 C 波段时改成RGates = 800、VGates = 1600、WGates = 1600。改错一个数,数组就越界或者读不全,这是血泪经验里最常见的一类翻车。
// 读取 S 波段基数据时的库数定义 const int RGates = 460; // 反射率距离库数 const int VGates = 920; // 速度距离库数 const int WGates = 920; // 谱宽距离库数 // 若要读取 C 波段基数据,改为: // const int RGates = 800; // const int VGates = 1600; // const int WGates = 1600;上面这段定义放在头文件里,本质是把“波段”这个物理事实翻译成“数组维度”这个程序事实。参数说明很直白:RGates 对应反射率那条数组的第三维长度,VGates 和 WGates 对应速度和谱宽。改的时候三个一起改,别只改一个,否则三块数据在内存里对不齐。
3.2 数据区布局:指针相同,偏移不同
两类雷达的信息头字段含义基本一致,指针字段的位置也一样(65-66 反射率、67-68 速度、69-70 谱宽),但指针指向的实际偏移值不同,因为前面的信息头加保留区长度虽然都是 128 字节左右,后面数据区的排布却随库数变化。SA/SB 的反射率数据从第 129 字节开始,速度从R+1开始,谱宽从V+1开始;CA/CB 同样是 129 字节起,但每块的长度拉长到 800、1600、1600。读的时候永远以指针为准,不要硬编码“反射率一定在 129”,虽然多数情况如此,但按指针走才是稳的。
3.3 仰角层数的坑:扫两次不等于两层
这是两类雷达共有的一个易混点,但特别值得单独拎出来说。SA、SB、CB 雷达在低层每个仰角上会扫描两次,比如 21 扫描模式的 VCP 仰角序列是 0.5、0.5、1.5、1.5、2.4、3.4、4.3、6.0、9.9、14.6、19.5,一共 11 次 PPI 扫描。但程序保存时,0.5 和 1.5 各扫的两次被合并记为一个仰角层,所以Elvation[]数组里只有 9 个有效元素:0.5、1.5、2.4、3.4、4.3、6.0、9.9、14.6、19.5。基数据实际有效层数是 9 层,不是 11 层。
如果你按“扫了几次就是几层”去分配数组,后面对不上号,图会叠在一起或者缺层。正确做法是看径向数据状态字段:状态 0 是该仰角第一条,状态 2 是最后一条,状态 3 是体扫第一条,状态 4 是体扫最后一条。靠这些状态位来切层,比数扫描次数可靠得多。
3.4 无效值与距离折叠:-999.0 和 999.0 不是随便定的
程序里把无效数据标记为 -999.0,距离折叠标记为 999.0。为什么用这两个数?因为它们远离真实物理量的合理范围——反射率再低也不会到 -999 dBZ,速度再大也不会到 999 米/秒,用这种极端值当“旗标”,后续判断时一眼就能认出来,不会和真实观测混淆。这跟数据库里用 NULL 表示缺失是一个思路:拿一个不可能出现的值占位,比额外维护一个布尔数组省事。读数据时先把这两个值识别出来,再做插值或绘图,否则它们会把色标拉爆,整张图糊成一团。
4. 常见误区与易混概念:读基数据时最容易踩的五个坑
4.1 把“字节位置”当成“数组下标”
易混点:信息头里写“35-36 字节是不模糊距离”,有人就直接拿数组第 35 个元素去读。为什么混:字节位置是从 1 开始数的文件偏移,数组下标是从 0 开始的内存位置,两者差一位,而且多字节字段还涉及高低字节顺序。怎么区分:记住“文件偏移从 1 数、数组下标从 0 数”,读双字节字段时先确认字节顺序(原文明确是双字节顺序),再拼成一个整数。差一位的后果是读出来的方位角永远偏一点,图看着“差不多”但就是不对。
4.2 把“径向序号”当成“方位角”
易混点:信息头里有“当前仰角内径向数据序号”(39-40 字节),也有“方位角”(37-38 字节),两个都是数字,容易拿来就用。为什么混:序号是第几条径向,方位角是天线指向多少度,前者是计数,后者是角度。怎么区分:序号用来排序和切层,方位角用来定位。程序里MaxRads按“每度保存一个径向”来设,说明最终要的是方位角,不是序号。用序号当角度,图会整体旋转错位。
4.3 把“扫描次数”当成“仰角层数”
易混点:前面说过,低层仰角扫两次,有人就认为层数等于扫描次数。为什么混:物理上确实扫了两次,但数据组织上合并为一层。怎么区分:看Elvation[]的有效元素个数,或者靠径向状态位切层。以 21 模式为例,11 次扫描对应 9 层,这个换算关系要刻在脑子里。
4.4 把“反射率库长”套到“速度库长”上
易混点:反射率库长 1000 米(S 波段),速度库长 250 米,有人图省事全用 1000 米去算距离。为什么混:都是“距离库长”,名字一样。怎么区分:反射率和多普勒是两套独立的距离刻度,第一个库的实际距离也分别存在 47-48 和 49-50 字节。算速度数据的距离时,必须用多普勒那套库长和首库距离,混用会让速度图的空间尺度整体拉伸四倍。
4.5 把“距离模糊”当成“无回波”
易混点:编码值 0 是无回波,编码值 1 是距离模糊,两个都是特殊值,有人统一当缺失处理。为什么混:都不套公式,看起来都“不是正常数据”。怎么区分:无回波是真的没信号,距离模糊是信号来自更远处、被折叠进来了,物理含义完全不同。做质量控制时,距离模糊点该单独标记,不能和无回波一起抹掉,否则会丢掉远处强回波的线索。
5. 什么时候该用这份格式说明,什么时候别硬套
先说该用的场景。如果你手上有一批 CINRAD SA/SB 或 CA/CB 的基数据文件,想把它还原成反射率、速度、谱宽的二维或三维场,这份格式说明就是必需的对照表——从 28 字节信息头逐字段解析,按指针定位三块数据区,按编码公式翻译成物理量,按库长和首库距离还原空间坐标,最后按径向状态位切仰角层。整个链路里每一步都依赖格式里的某个字段,缺一环就断。尤其是做历史数据回算、做算法验证、做教学演示,这种“从字节到物理量”的完整还原能力是绕不开的基本功。
再说别硬套的场景。第一,如果你只是要看现成的雷达图产品,没必要碰基数据——产品级数据已经做过坐标变换、质量控制、拼图,直接拿来用效率高得多,硬啃基数据属于用放大镜看报纸,费力不讨好。第二,不同雷达型号、不同年代的格式可能有细微差异,这份说明覆盖的是 SA/SB 和 CA/CB 两类,遇到别的型号别想当然套用,先确认字段定义是否一致。第三,格式里的保留字段(比如 1-14、17-28、75-82、91-128 这些)不要自作主张去解读,保留就是保留,里面可能是历史遗留或厂商私有内容,硬读出来的东西没有意义,还可能误导后续判断。
我自己的习惯是:每次拿到一批新来源的基数据,先不急着写解析逻辑,而是先抽一条径向,把 28 字节信息头逐字段打印出来,核对数据类型标识是不是 1、方位角和仰角算出来是否落在合理范围、三个指针是否指向文件内合法位置、库数和波段是否匹配。这一遍走完,心里就有底了,后面批量处理才不容易翻车。从那以后我每次读陌生格式的基数据,都强制先走一遍这个“单条径向体检”,省下的调试时间远比这一步花的多。希望这套思路帮到你。
如果你想把这份格式说明连同字段表、编码公式、数组定义一起留着随时对照,完整资料已经整理好,按文末方式获取即可,读基数据的时候摊在手边,比来回翻网页踏实。
本文还有配套的精品资源,点击获取