Files
MapLibreParticleLab/docs/30_设计/WeatherV2_QBIN_V3通用天气场二进制格式与React独立Render_Binding设计_v2.2_最终冻结稿.md
2026-09-21 08:03:39 +09:00

90 KiB
Raw Blame History

WeatherV2 QBIN V3 通用天气场二进制格式与 React 独立 Render Binding 设计

文档修订:v2.2
QBIN Container Version:3
Magic:QBV3
日期:2026-09-20
状态:设计冻结候选 / 可进入 Writer + C++ Decoder Prototype;正式发布前仍需冻结 Block Geometry、完成 Round-trip 与 Native 性能验证
适用范围:WeatherV2 → NavSea → MapLibreParticle
上一代权威基线:WeatherV2_QBIN_V2通用天气场二进制格式与React独立Render_Binding设计_v4.4.md 实施前补充依据:WeatherV2 QBIN V3 实施前补充要求(给 Weather 生产方) v1.0

v2.2 相对 v2.1 的最终文档清理:

1. 修正验收表对 non-finite physical sample 的表述:
   validity=1 时 physical MUST finite;
   validity=0 时 numeric physical value 不参与 Quantizer,
   可为 NaN / Inf / missing。

2. 将 Corruption / Fail-closed 验收拆分为:
   Binary Reject Cases
   Binary Accept Cases
   Writer Validation Cases
   避免把 MUST accept / Writer-only 行为误写成统一 fail-closed。

3. 不再新增任何 QBV3 Binary ABI 设计项。
   除最终 BLOCK_WIDTH / BLOCK_HEIGHT 仍需 benchmark 后冻结外,
   v2.2 作为进入 Writer + Decoder Prototype 的最终设计冻结稿。

v2.1 相对 v2.0 的最终收口重点:

1. Rice canonicality 正式冻结为 Writer-canonical / Decoder-permissive:
   Writer MUST 计算并写入最优 k*;
   Decoder 只验证 riceK∈[0,31] 与 bitstream / symbolCount / padding /
   residual / raw range 等合法性,不为 canonicality 重算 32 个候选 k。
   非最优但可合法解码的 riceK 不属于 QBV3 binary corruption。

2. WEB_MERCATOR + NODE 的数学定义补全:
   直接冻结 dimensionless spherical Web Mercator
   X=λ,Y=ln(tan(π/4+φ/2)),
   并给出 inverse 公式,消除“Mercator”实现歧义。

3. VALIDITY semantic 增加双向约束:
   semantic==3 ⇔ valueClass==VALIDITY ⇔ dtype==BITSET;
   semantic=3 不得被 Numeric Plane 复用。

4. Quantizer 增加 intermediate finite 要求:
   delta64 / normalized64 / normalizedDirection64 MUST finite;
   否则 Writer FAIL。

5. fieldType=0 明确冻结为 UNKNOWN / UNSPECIFIED opaque hint,
   binary-valid,不得单独导致 reject。

6. 清理正文中遗留的 v1.9 / v2.0 临时字样,并连续整理后半部章节编号。

7. v2.1 之后,除最终 BLOCK_WIDTH / BLOCK_HEIGHT 仍需通过
   16×16 / 32×32 C++ benchmark 实测选择外,
   不再保留其他核心 QBV3 Binary ABI 未决语义。

v2.0 相对 v1.9 的最终收口重点:

1. Invalid-cell Writer 语义冻结:
   validity=1 的 physical sample MUST finite;
   validity=0 的 physical payload value 被忽略,可为 NaN/Inf/missing,
   不参与 Quantizer、不产生 Rice symbol,decoded canonical raw 固定为 0。
   Writer 不得通过 NaN/Inf 自动推导 validity=0。

2. ZigZag / inverse ZigZag 冻结为精确数学公式,
   不再依赖“标准 ZigZag”约定或实现自行解释。

3. Quantizer binary64 计算语义收口:
   Descriptor binary32 scale/offset 先提升为 binary64;
   subtraction / division 使用 IEEE-754 binary64 round-to-nearest,
   ties-to-even 语义后,再执行 round_half_away_from_zero。
   Direction Writer 要求 canonical input 已归一化到 [0,360),
   QBV3 Writer 不承担天气输入清洗。

4. unknown fieldType 的 Binary 行为冻结:
   fieldType 是 opaque uint16 hint;
   未知值 MUST NOT solely cause binary rejection,
   保证所有 conforming Decoder 对同一文件得到一致 Accept/Reject。

5. Numeric offset canonical zero 规则全局化:
   任意 Numeric Plane 若 offset 数值等于 0,
   MUST 使用 binary32 +0.0;-0.0 MUST reject。

6. Validity BITSET 增加精确索引公式:
   byteIndex=index>>3, bitIndex=index&7,
   valid=(byte>>bitIndex)&1。

7. 修正文档遗留:
   §17 的 v1.8 字样统一为 v2.0;
   v2.0 之后,除 BLOCK_WIDTH/BLOCK_HEIGHT benchmark freeze 外,
   不再保留其他核心 Binary ABI 未决语义。

v1.9 相对 v1.8 的收口重点:

1. round_half_away_from_zero 冻结为明确数学公式;
   Quantizer 使用 Descriptor 已确定的 binary32 scale/offset 提升到 binary64 计算,
   non-finite physical sample Writer MUST reject。
2. 补齐 Descriptor byte-level structural invariants 与必要机器值:
   valueClass / component / core unit / VALIDITY semantic 的 Container 约束集中定义。
3. referenceTimeUtc / validTimeUtc 只要求各自为正 Unix UTC seconds;
   QBV3 Container 不规定 validTimeUtc 与 referenceTimeUtc 的相对大小。
4. CRC32 参数完全展开:
   CRC-32/ISO-HDLC,poly=0x04C11DB7、reflected poly=0xEDB88320、
   init=0xFFFFFFFF、refin/refout=true、xorout=0xFFFFFFFF。
5. Block partition 数学公式冻结;
   最终只需在 16×16 / 32×32 benchmark 后回填 BLOCK_WIDTH / BLOCK_HEIGHT。
6. 当前 Parser conformance limits 继续作为 QBV3 Version 3 normative acceptance limits;
   现阶段容量确认够用。未来如需扩大,不在已发布 Version 3 上静默放宽,
   应通过后续 Container Version / successor spec 明确升级。
7. 将 ContentIdentity、QBV2 legacy validator、Manifest、Distribution、
   Runtime/Binding 等非 Binary ABI 内容进一步移出 normative 主体,
   只保留为 Non-normative Appendix。
8. v1.9 之后,Block Geometry 是正式发布前唯一尚未通过实测冻结的核心 Binary ABI 参数。

v1.8 相对 v1.7 的收口重点:

1. 明确 QBV3 binary-valid 与外部 Profile-valid 是两层:
   QbinV3Decoder 只负责 Container / byte-level validation;
   Profile Contract validation 属于 Encoded Field Ingress,
   不属于 QBV3 Binary ABI,也不得被称为 binary corruption。
2. 在 normative 主体补全 DType byte-level enum:
   1=INT16, 2=UINT16, 3=UINT8, 4=INT8, 5=FLOAT32, 6=BITSET;
   QBV3 Numeric 仅允许 1/2/3/4,Validity 固定 6,FLOAT32 在 V3 第一版 reject。
3. 补全 fieldType byte-level machine hint 语义与已知值;
   fieldType 不得决定 Plane layout / Profile completeness / Rice 解释。
4. 补全 WEB_MERCATOR + NODE Grid 的精确空间含义:
   Header bounds 为 WGS84 lon/lat degrees,
   Grid sample 在 Web Mercator projected space 等间距;
   row0=north,col0=west,index=row×width+col。
5. 冻结 QBV3 Parser conformance limits:
   MAX_PLANE_COUNT=32、
   MAX_CELL_COUNT=2,097,152、
   MAX_QBIN_BYTES=64 MiB、
   MAX_DECODED_BYTES_PER_FRAME=32 MiB。
6. Header float64 Bounds 增加 canonical zero:
   数值为 0 时 MUST serialize 为 IEEE-754 binary64 +0.0,
   禁止 -0.0,保证 byte-for-byte deterministic Writer。
7. Block Geometry 仍是唯一尚未冻结的核心 Binary ABI 参数;
   v1.8 不擅自选择 16×16/32×32,正式 production QBV3/version=3
   仍必须等 C++ benchmark 后回填唯一尺寸。

v1.7 相对 v1.6 的收口重点:

1. 文档规范范围重新收口到 QBIN V3 Binary Container 本身;
   NavSea / React / Store / Binding / Renderer / 平台 Bridge / Contract JSON
   等消费与集成内容移出 normative 主体,仅保留为 Non-normative Integration Notes。
2. Quantization 冻结为“先序列化 float32,再用该 float32 值量化”:
   Descriptor 中 scale / offset 的 IEEE-754 binary32 bit-pattern
   是 Quantizer 的唯一参数来源,禁止使用序列化前更高精度配置值直接量化。
3. Shared Validity 明确采用两阶段逻辑解析:
   先解析全部 Descriptor 并定位唯一 Validity Plane,
   再验证/读取 Validity,最后解 Numeric Rice Plane;
   不要求 Validity 在 payload 中物理排在 Numeric Plane 前面。
4. Numeric Plane byteLength 精确定义为所有 non-empty block 的
   `1-byte riceK + ceil(RiceSymbolBits/8)` 之和,
   不包含 Plane 间 align16 padding、Descriptor 或其他 Plane。
5. Rice k 的 deterministic 选择公式正式冻结:
   `bits(k)=Σ((z_i>>k)+1+k)`,k∈[0,31];
   只按 Rice symbol bit count 比较,tie 选较小 k,
   不把 1-byte riceK 与 byte-boundary padding 纳入优化目标。
6. QBV3 Header 的 FRAME 固定值直接写入本规范:
   layout=1、projection=1、sampleOrder=1、gridRegistration=1、
   tileZ=0xffff、tileX=0、tileY=0;
   referenceTimeUtc / validTimeUtc 明确为正 int64 Unix UTC seconds。
7. Descriptor canonical float 增加 +0.0 规则:
   语义要求 offset=0 时 MUST 写 binary32 +0.0 (0x00000000),
   Validity 固定 scale=+1.0、offset=+0.0,禁止 -0.0。
8. 第一阶段验收表同步收口为 Binary ABI / Writer / Parser /
   Round-trip / Codec Benchmark,不再把消费侧行为作为 QBV3 冻结条件。

v1.6 相对 v1.5 的收口重点:

1. Field Profile Contract Schema 升级方向明确:
   为 required Plane 增加 expectedDType / expectedScaleBits / expectedOffsetBits,
   使 wind/2、wave/2、rain/2、current/2 的量化参数由 Profile Contract
   data-driven 校验,而不是只依赖 Container 或 Generic Unit 验证。
2. MAX_DECODED_BYTES_PER_FRAME 口径补齐 Validity:
   decodedFrameBytes = decodedNumericBytes + decodedValidityBytes,
   validityPlane.storage / validityBits 同一 storage 只计一次。
3. Direction N 固定为 floor(ratioToN + 0.5),不依赖 host FPU rounding mode。
4. QBV2 frozen behavior 定义为 authoritative ingress acceptance 不变。
5. MAX_RETAINED_DECODED_BYTES 仅约束 EncodedFieldStore 当前 retained Frames 可达的 canonical decoded storage。
6. Validity ContentIdentity 冻结为与 Numeric Plane 相同的 hash algorithm / input order,并调用同一 identity helper。

v1.5 相对 v1.4 的收口重点:

1. QBV2 Direction legacy acceptance 精确对齐当前 C++:
   保留 canonical=360/65536 与 4×float epsilon tolerance;
   QBV3 继续使用 canonical float32(360/N) bit-pattern,二者不互相放宽或收紧。
2. Plane ContentIdentity 进一步冻结当前 QBV2 的实际 hash 常数、
   uint64 wrap 语义、逐 byte XOR→multiply 顺序与最终 hex 输出格式;
   禁止“顺手修正”为标准 FNV-1a offset basis。
3. EncodedFieldIngress::decodeAndValidate() 升级为正式运行时唯一 authoritative
   decode + contract validation 入口;RasterArraySource::submitEncodedField()
   不得继续直接调用 QbinV2Decoder。
4. MAX_RETAINED_DECODED_BYTES 明确为 V3 实施阶段新增 Store safety policy,
   不是当前 v4.4 已存在行为;按唯一 retained storage 计数,
   shared_ptr alias / validityPlane.storage 与 validityBits 不重复计费。
5. 补齐 v1.3 revision history,并把当前版本引用统一更新为 v1.5。

v1.4 相对 v1.3 的收口重点:

1. Direction validation 分为 Container-specific + Generic 两层:
   QBV2 保留 legacy 360/65536° frozen acceptance;
   QBV3 使用 Generic Circular Contract,不反向放宽 V2。
2. Plane ContentIdentity 冻结为与当前 QBV2 完全相同的 hash algorithm、
   input byte order 与 field order。
3. MAX_DECODED_BYTES_PER_FRAME 由 Decoder 在 allocation 前检查;
   MAX_RETAINED_DECODED_BYTES 由 EncodedFieldStore / owning Store layer
   在接受 Frame 前检查。
4. all-invalid 下 zero-length Numeric Plane 的 canonical packing 明确允许
   相邻 zero-length Plane / 下一 non-empty Plane 共享 byteOffset。
5. Direction canonical scale 的 float64→float32 转换固定为
   IEEE-754 round-to-nearest, ties-to-even;
   计算 N 前先验证 ratio finite 且处于允许范围。

v1.3 相对 v1.2 的收口重点:

1. 明确 FieldSeries::submit() 遇到不兼容 Frame 时返回
   RejectedIncompatibleFrame;V2→V3 / schema 切换由上层 clear + rebuild,
   不假设 Store 自动 replacement/reset。
2. Direction decode 除 UINT16 dtype range 外,额外要求
   0 <= rawDirection < N;超出 Circular Contract 必须 fail-closed。
3. 修正 Rice bitstream 章节引用,并统一文档状态/版本号为 v1.3。

v1.2 相对 v1.1 的收口重点:

1. Direction scale 改为 canonical float32(360/N) bit-pattern 校验;固定 half-circle tie=-N/2,并给出整数 circular 参考公式。
2. Plane / Validity ContentIdentity 精确对齐当前 QBV2 C++ hash input;Grid identity 保持独立。
3. 不新增 FieldSeriesCompatibilityKey;继续以 DecodedFieldFrame::isInterpolationCompatibleWith() 的 canonical schema + grid 为权威。
4. 固定 Parser 顺序:结构+Budget → CRC → Validity → Rice decode。
5. 增加 dtype/circular logical residual 上界与 unary MAX_Q hardening。
6. Block Geometry 未冻结的实验文件禁止进入 production Manifest/CDN qbin_v3;V2/V3 benchmark 使用同一 CRC 实现。

0. 与 QBIN V2 / v4.4 的关系

本文是 QBIN V3 Container 的第一版设计文档修订版。

上一代权威设计基线为:

WeatherV2_QBIN_V2通用天气场二进制格式与React独立Render_Binding设计_v4.4.md

其中:

文档设计版本:v4.4
QBIN Container Version:2
Magic:QBV2

本文中的 QBIN V3 是上述 QBIN V2 / v4.4 基线的直接后继版本。

0.1 继承关系

QBIN V3 不重新设计 WeatherV2 → NavSea → MapLibreParticle 的整体架构。

除本文明确修改的 Container / Encoded Plane 编码规则外,QBIN V2 v4.4 已冻结的下列架构原则继续有效:

WeatherV2
负责 Provider 获取、物理语义归一化、Canonical Unit、
Quantization、QBIN Writer、Manifest 和协议数据。

NavSea / React
负责 Manifest / Registry / Field Profile Contract Resolve、
fieldKey、下载、缓存、预取、requestedForecastTime、
Color Binding 和 Particle Binding。

MapLibreParticle Encoded Field Ingress
负责 QBIN Parser / Decoder、CRC、Generic Field Profile Validator,
并输出格式无关的 DecodedFieldFrame / Plane Registry。

Runtime / Renderer
继续只处理通用 Scalar / Velocity / Direction / Validity,
不认识 Weather Provider、Manifest 或具体天气业务类型。

以下 v4.4 原则继续冻结:

Semantic Registry
Unit Registry
Field Profile Contract
ResolvedFieldProfileContract

Canonical Unit
Value Class
Component / groupId

REQUIRED 完整性规则
Unknown Profile / Unknown Scalar Semantic 通用处理

Color Binding 与 Particle Binding 独立
Color Projection 与 Particle Projection 独立

Core-owned Forecast Clock / Playback
每个 fieldKey 独立 Runtime Field Window
Native Core 自行解析 T0 / T1 / mix

FrameContentIdentity
generation / dataVersion 修订识别

Renderer 不增加:
if WIND
if WAVE
if RAIN
等天气业务分支

因此:

QBIN V2 Decoder
        ↓
DecodedFieldFrame
        ↓
Runtime / Binding / Renderer

升级为:

QBIN V3 Decoder
        ↓
DecodedFieldFrame
        ↓
Runtime / Binding / Renderer

Decoder 之后的公共架构原则保持不变。

0.2 V3 为什么需要新的 Container Version

QBIN V2 将 Numeric Plane 定义为固定宽度的直接量化整数数组,例如:

INT16  → byteLength = count × 2
UINT16 → byteLength = count × 2

Numeric Plane 的 byteLength 可以直接由:

count × dtype size

推导。

QBIN V3 引入新的 Numeric Plane 编码:

Physical Value
↓
Quantization
↓
Integer Grid
↓
2D Spatial Predictor
↓
Signed Residual
↓
ZigZag
↓
固定 Block Geometry 的 adaptive Rice Coding
↓
Variable-length Encoded Plane

因此 Numeric Plane 的编码长度不再等于:

count × dtype size

而成为真正的:

variable-length encoded payload

这改变了 QBIN V2 的二进制 Plane Contract,因此不能继续标记为 Container Version 2。

正式升级为:

QBIN Container Version:3
Magic:QBV3
Version:3

0.3 V3 不代表业务协议重做

Container Version 升级只表示:

Encoded Binary Representation

发生不兼容变化。

它不意味着以下协议重新开始:

Semantic ID
Unit ID
Value Class
Component
groupId
Field Profile
Render Binding
Forecast Runtime
Renderer

这些仍继承 v4.4 的长期通用设计。

因此:

Container Version

和:

Field Profile Version

必须继续独立管理。

第一阶段建议:

QBIN V3

wind / 2
wave / 2
rain / 2
current / 2

表示:

Container 使用 QBIN V3
+
各天气 Field 使用自己的 Profile Version

而不是把天气业务规则硬编码进 Container Version。

0.4 v4.4 文档的历史地位

WeatherV2_QBIN_V2通用天气场二进制格式与React独立Render_Binding设计_v4.4.md

从 QBIN V3 生效后仍永久保留,作为:

QBIN V2 最终冻结规范
QBIN V3 架构来源基线
旧 QBV2 文件解释依据
V2/V3 兼容与迁移依据

不得覆盖、改名或修改其已经冻结的历史定义。

本文只定义:

QBIN V3

以后对 V3 的小幅修订使用本文自身的:

Document Revision

管理,例如:

v1.0
v1.1
v1.2

不得再用文档修订号代替 Container Version。

因此版本体系统一为:

QBIN V2
└── 最终设计基线:v4.4

QBIN V3
└── 本文初始设计版本:v1.0

0.5 本文的 Normative Scope

从 v1.7 开始,本文的 normative 主体只定义:

QBV3 Header
Plane Descriptor
Endian / canonical float
Quantization
Shared Validity
2D Predictor
Direction Circular Predictor
ZigZag
Rice Block / bitstream
Numeric Plane byteLength
Plane Packing / Alignment
CRC32
Parser Safety / Fail-closed
Deterministic Writer
Golden Binary / bit-exact Round-trip
Codec encode/decode benchmark

下列内容不属于 QBV3 Binary ABI:

NavSea 如何选择 / 下载 / 缓存 QBV3
React / RN API
Profile Contract JSON 的 wire schema
EncodedFieldStore / FieldSeries
T0 / T1 / mix
Binding / Projection / Renderer
Android / iOS Bridge transport
Store retained-memory policy
业务层 V2→V3 切换流程

这些内容只可作为 Non-normative Integration Notes 保存,不能反向改变同一个:

magic = QBV3
version = 3

的文件解释。

目标是:

Weather 生产方只阅读本文 normative 主体,就能生成 byte-for-byte deterministic、跨平台可解码、可严格校验的 QBV3 文件;消费方实现细节不属于 Container 语义。


1. V3 设计目标

QBIN V3 的核心目标只有一个:

在保持 v4.4 通用天气场架构不变的前提下,大幅降低每个时间 Frame 的网络与缓存体积,并保持 Native 解码简单、确定、可校验。

设计目标:

1. 一个 targetTime 仍对应一个独立 Frame 文件。
2. 不把多个时间点打成一个大归档。
3. NavSea 可只下载当前 T0/T1 和必要预取 Frame。
4. 数值误差只来自明确的 Quantization Profile。
5. Predictor / ZigZag / Rice 全部可逆,不增加额外物理误差。
6. Decoder 之后仍输出既有 DecodedFieldFrame / Plane Registry。
7. Runtime / Renderer 不感知 QBIN V2/V3 差异。
8. Wind / Wave / Rain / Current 共用同一 Container 与编码方法。
9. 不为每种天气增加独立二进制格式。
10. Writer 必须 deterministic;相同输入与同一 Profile 产生 byte-for-byte 相同文件。

2. 不在 V3 中解决的问题

V3 不负责:

颜色表
粒子颜色
粒子数量
Trail
UI
Provider 网络请求
Route / ETA 业务
Forecast 下载调度
缓存淘汰策略

V3 也不改变:

ColorBinding / ParticleBinding
Core Forecast Clock
FieldSeries
T0 / T1 / mix
GPU Renderer

3. 最终数据链

Provider
   ↓
WeatherV2 Normalizer
   ↓
Server Canonical Field
   ↓
Unified Quantizer / Field Profile
   ↓
QBIN V3 Writer
   ↓
2D Predictor
   ↓
ZigZag
   ↓
固定 Block Geometry 的 adaptive Rice
   ↓
QBV3 Frame
   ↓
HTTP Distribution Encoding(可 gzip)
   ↓
NavSea Download / Cache
   ↓
MapLibreParticle Encoded Field Ingress
   ↓
QbinV3Decoder
   ↓
DecodedFieldFrame / Plane Registry
   ↓
EncodedFieldStore / FieldSeries
   ↓
Binding Resolver
   ├── Color Projection
   └── Particle Projection
   ↓
Core-owned Forecast Clock / Runtime Field Window
   ↓
WeatherColorLayer / RasterParticleLayer

QBIN V2 与 V3 的分界只允许存在于 Encoded Field Ingress:

QBV2 → QbinV2Decoder ┐
                     ├→ DecodedFieldFrame
QBV3 → QbinV3Decoder ┘

不得把 V3 编码细节泄漏到 Runtime/Renderer。


4. 文件总体结构

V3 第一版继续采用简单固定结构:

┌──────────────────────────────┐
│ Fixed Header                 │ 112 bytes
├──────────────────────────────┤
│ Plane Descriptor Table       │ 32 bytes × planeCount
├──────────────────────────────┤
│ Payload                      │ variable
└──────────────────────────────┘

继续不保存:

headerSize
descriptorSize
descriptorOffset
payloadOffset
totalLength

固定:

HEADER_SIZE = 112
DESCRIPTOR_SIZE = 32

descriptorOffset = 112
payloadOffset = 112 + planeCount × 32
expectedTotalLength = payloadOffset + payloadLength

V3 第一版不利用 Header/Descriptor 的 reserved 字段承载新业务信息。

所有 reserved 字段仍必须:

0

这样保留二进制结构的简单性。


5. Magic / Version / Endian

magic = ASCII "QBV3"
version = 3

未知 Version:

fail-closed

所有 multi-byte integer 与 IEEE-754 floating point:

Little Endian

Rice bitstream 的 bit order 由本文第 18 节单独定义,不使用 host-native bitfield。

5.1 QBV3 第一版 FRAME 固定 Header ABI

QBV3 第一版只定义:

layout = FRAME

对应 Header 固定值:

layout           = 1
projection       = 1   # WEB_MERCATOR
sampleOrder      = 1   # NORTH_TO_SOUTH_WEST_TO_EAST
gridRegistration = 1   # NODE

tileZ = 0xffff
tileX = 0
tileY = 0

以下时间字段:

referenceTimeUtc
validTimeUtc

固定为:

int64
Unix UTC seconds
MUST > 0

不得在 Header 中直接写毫秒。

QBV3 Container 不规定二者的相对大小:

不得在 Binary Parser 中强制:
validTimeUtc >= referenceTimeUtc

以下情况都可以由外部 Profile / Dataset 语义决定:

Forecast
Analysis
Observation
Reanalysis / Hindcast

因此 Binary-valid 只要求两个时间字段各自满足格式与范围要求, 不因 validTimeUtc < referenceTimeUtc 自动判定 corruption。

Bounds:

west / south / east / north

必须是 finite float64,第一版 WEB_MERCATOR FRAME 至少满足:

-180 <= west < east <= 180
-85.0511287798066 <= south < north <= 85.0511287798066

未知 layout / projection / sampleOrder / gridRegistration:

fail-closed

这些值直接属于 QBV3 Container ABI,不要求 Writer 通过查阅 QBIN V2 文档才能生成合法 Header。

5.2 WEB_MERCATOR + NODE Grid 的精确空间语义

Header:

west / south / east / north

是:

WGS84 longitude / latitude degrees

但 Grid node 在南北方向的等间距不是按 latitude degree 线性插值, 而是在 Web Mercator projected space 中等间距。

定义数学上的 dimensionless spherical Web Mercator:

λ = longitudeDeg × π / 180
φ = latitudeDeg  × π / 180

mercatorX(longitudeDeg) =
    λ

mercatorY(latitudeDeg) =
    ln(
        tan(
            π/4 + φ/2
        )
    )

其 inverse:

longitudeDeg =
    X × 180 / π

latitudeDeg =
    (
        2 × atan(exp(Y))
        - π/2
    ) × 180 / π

由于 Header 已限制:

-85.0511287798066 <= latitude <= 85.0511287798066

正文不再额外定义 latitude clamp。

若某实现使用 EPSG:3857 meter coordinate:

X_meter = 6378137 × X
Y_meter = 6378137 × Y

其 Grid node 位置与本文 dimensionless 公式完全等价; 规范语义以本文 dimensionless 公式为准。

实现用于 Grid sample 定位时应采用 IEEE-754 binary64 计算。

因此:

xWest  = mercatorX(west)
xEast  = mercatorX(east)

yNorth = mercatorY(north)
ySouth = mercatorY(south)

对于 NODE grid:

dx = (xEast  - xWest)  / (width  - 1)
dy = (yNorth - ySouth) / (height - 1)

逻辑 sample (row, col):

x = xWest  + col * dx
y = yNorth - row * dy

因此:

row = 0
→ north edge

row = height - 1
→ south edge

col = 0
→ west edge

col = width - 1
→ east edge

flattened cell index:

index = row * width + col

sample order:

NORTH_TO_SOUTH_WEST_TO_EAST

等价于:

row-major
每一 row 内 west → east
rows north → south

Writer / Decoder / Golden Fixture 不得把 south..north latitude degree 直接做线性 sample spacing 来替代上述 Web Mercator projected-space 语义。


6. Fixed Header

V3 第一版继续沿用 v4.4 的 112-byte Header 字段位置,只修改 Magic / Version:

Offset Size Type Field
0 4 char[4] magic=QBV3
4 1 uint8 version=3
5 1 uint8 layout
6 2 uint16 fieldType
8 1 uint8 projection
9 1 uint8 sampleOrder
10 1 uint8 gridRegistration
11 1 uint8 reserved0=0
12 2 uint16 planeCount
14 2 uint16 reserved1=0
16 4 uint32 width
20 4 uint32 height
24 8 int64 referenceTimeUtc
32 8 int64 validTimeUtc
40 8 float64 west
48 8 float64 south
56 8 float64 east
64 8 float64 north
72 4 uint32 payloadLength
76 4 uint32 fileCrc32
80 2 uint16 tileZ
82 2 uint16 reserved2=0
84 4 uint32 tileX
88 4 uint32 tileY
92 4 uint32 reserved3=0
96 4 uint32 reserved4=0
100 4 uint32 reserved5=0
104 4 uint32 reserved6=0
108 4 uint32 reserved7=0

时间、Bounds、Projection、Sample Order、Grid Registration 的语义继续继承 v4.4。

6.1 fieldType:Machine Hint,不是 Layout Authority

fieldType 是 uint16 machine-readable hint,沿用上一代已知值:

1 = WIND
2 = WAVE
3 = CURRENT
4 = RAIN
5 = AIR_TEMPERATURE
6 = SEA_TEMPERATURE
7 = AIR_PRESSURE
8 = HUMIDITY
9 = VISIBILITY

未知 fieldType:

MUST NOT solely cause binary rejection

只要 Header / Descriptor / Plane / CRC 等其他 QBV3 Binary Contract 全部合法, Generic QBV3 Parser 必须继续按 binary-valid 处理。

也就是说 fieldType 是:

opaque uint16 machine hint

已知值具有当前约定含义,未知值仍保持 forward-compatible hint 语义。

特别冻结:

fieldType = 0
= UNKNOWN / UNSPECIFIED
= binary-valid opaque hint

fieldType=0 或其他未知 uint16 值都不得单独导致 Binary reject。

fieldType MUST NOT 决定:

Plane count
Plane semantic set
dtype
scale / offset
REQUIRED completeness
Rice block interpretation
Profile compatibility
Renderer behavior

因此:

fieldType == WIND

不能替代外部:

fieldProfileId = wind
fieldProfileVersion = 2

也不能使 Parser 自动要求 U/V Plane。

fieldType 与具体 Profile 是否一致属于外部 Profile / Product validation, 不属于 QBV3 binary corruption。

6.2 Header Bounds 的 Canonical Float64 Zero

west / south / east / north 使用 IEEE-754 binary64 little-endian。

为了 deterministic Writer:

如果任一 bound 的数学值 == 0:
MUST serialize as +0.0 binary64

bit-pattern:
0x0000000000000000

禁止:

-0.0
bit-pattern = 0x8000000000000000

其他有限非零值按其 canonical binary64 value 写入。


7. Plane Descriptor

V3 第一版继续保持每个 Descriptor 32 bytes:

Offset Size Type Field
0 2 uint16 semantic
2 1 uint8 dtype
3 1 uint8 flags
4 4 float32 scale
8 4 float32 offset
12 4 uint32 count
16 4 uint32 byteOffset
20 4 uint32 byteLength
24 2 uint16 unit
26 2 uint16 groupId
28 1 uint8 valueClass
29 1 uint8 component
30 2 uint16 reserved=0

7.1 DType Byte-level Enum

QBV3 dtype 是 uint8,byte-level 值冻结为:

1 = INT16
2 = UINT16
3 = UINT8
4 = INT8
5 = FLOAT32
6 = BITSET

QBV3 Version 3 第一版合法组合:

Numeric Plane:
dtype ∈ {INT16, UINT16, UINT8, INT8}
即 byte value ∈ {1,2,3,4}

Validity Plane:
dtype = BITSET
即 byte value = 6

以下在 QBV3 Version 3 第一版必须 reject:

Numeric dtype = FLOAT32 (5)
Numeric dtype = BITSET  (6)

Validity dtype != BITSET (6)

unknown dtype

FLOAT32=5 的 enum 值保留是为了保持跨代 registry / decoded type 一致性, 但它不代表 QBV3 Version 3 第一版允许 FLOAT32 Rice Plane。

V3 与 V2 的关键区别:

Numeric Plane

dtype 表示 解码后的逻辑 Quantized Integer 类型,不是 Payload 的固定宽度存储长度。

count = width × height
byteLength = 该 Plane 的实际 Rice encoded byte 数

因此 V3 Numeric Plane 不再要求:

byteLength == count × dtypeSize

Validity Plane

第一版仍保持直接 BITSET:

dtype = BITSET
count = width × height
byteLength = ceil(count / 8)

7.2 Descriptor Canonical Float Representation

Descriptor 中:

scale
offset

都是 IEEE-754 binary32 little-endian。

QBV3 deterministic Writer 必须使用 canonical binary32 representation。

特别冻结:

如果某语义要求 offset == 0:
    offset MUST = +0.0f
    bit-pattern = 0x00000000

不得写:
    -0.0f
    bit-pattern = 0x80000000

Validity Plane 固定:

scale  = +1.0f
bit-pattern = 0x3f800000

offset = +0.0f
bit-pattern = 0x00000000

对于所有 Numeric Plane:

scale MUST finite
scale > 0
offset MUST finite

并冻结全局 canonical zero:

if offset == 0.0 numerically:
    bit-pattern MUST = +0.0f
    0x00000000

-0.0f
0x80000000
→ MUST reject

该规则不依赖外部 Profile 是否显式声明“offset=0”。

该规则属于 binary canonicalization,目的包括:

byte-for-byte deterministic Writer
CRC 稳定
Content Identity 稳定
跨语言 / 跨平台一致

7.3 Descriptor Byte-level Structural Invariants

为了让 QBV3 Binary Parser 不依赖外部 Profile 名称, 以下机器值与通用结构约束直接属于 Container Version 3。

Value Class

0 = SCALAR
1 = VELOCITY_COMPONENT
2 = DIRECTION_ANGLE
3 = VALIDITY

未知 valueClass:

MUST reject

Component

0 = NONE
1 = U_EAST
2 = V_NORTH
3 = W_UP

未知 component:

MUST reject

Core Unit IDs

QBV3 第一版直接使用:

0 = NONE
1 = MPS
4 = DEGREE

Scalar 的其他 unit ID 可以由外部 Unit Registry 定义; Binary Parser 不因为“不认识某个 Scalar unit ID”而自动判定 corruption。

Core Semantic

Shared Validity 的 frozen core semantic:

VALIDITY semantic = 3

并冻结双向约束:

semantic == 3
⇔ valueClass == VALIDITY
⇔ dtype == BITSET

因此:

semantic = 3
+ SCALAR / VELOCITY_COMPONENT / DIRECTION_ANGLE
→ MUST reject

valueClass = VALIDITY
+ semantic != 3
→ MUST reject

dtype = BITSET
+ valueClass != VALIDITY
→ MUST reject

一个合法 QBV3 Frame 仍必须有且只有一个满足上述关系的 Shared Validity Plane。

Numeric semantic:

semantic = 0
→ reserved / invalid

除 semantic=3 已被冻结为 VALIDITY 外, 其他 Numeric semantic ID 的业务含义由外部 Semantic Registry 决定。

通用组合约束

SCALAR:
    dtype ∈ {INT16, UINT16, UINT8, INT8}
    component = NONE

VELOCITY_COMPONENT:
    dtype ∈ {INT16, UINT16, UINT8, INT8}
    unit = MPS
    component ∈ {U_EAST, V_NORTH, W_UP}
    groupId != 0

DIRECTION_ANGLE:
    dtype = UINT16
    unit = DEGREE
    component = NONE
    groupId = 0
    offset = canonical +0.0f
    scale obeys 第 15 节 Circular Contract

VALIDITY:
    semantic = 3
    dtype = BITSET
    unit = NONE
    groupId = 0
    component = NONE
    flags.REQUIRED = 1
    scale = canonical +1.0f
    offset = canonical +0.0f

一个 Frame:

MUST 有且只有一个 VALIDITY Plane

Binary Parser 不负责验证:

某天气 Profile 是否需要 U/V 成对
某 Scalar semantic 是否 REQUIRED
某 Field Profile 是否允许 additional Plane

这些属于外部 Profile Contract,而不是 QBV3 corruption。

8. Value Class / Semantic / Unit / groupId

QBV3 Binary Parser 使用第 7.3 节冻结的:

valueClass
component
core unit
VALIDITY semantic

作为 byte-level structural contract。

外部 Semantic Registry / Unit Registry 可以为:

Scalar semantic
Scalar unit
未来扩展语义

赋予更具体含义,但不得改变 QBV3/version=3 已冻结的 byte-level 解释。

groupId 的 Container 级基本规则:

VELOCITY_COMPONENT → groupId != 0
DIRECTION_ANGLE    → groupId = 0
VALIDITY           → groupId = 0

SCALAR 的 groupId 是否具有额外业务含义由外部 Profile 决定。

V3 不因为编码变化增加:

if WIND
if WAVE
if RAIN
if CURRENT

等天气业务分支。

9. REQUIRED Flag 与 Binary-valid / Profile-valid 分层

Descriptor flags:

bit0 = REQUIRED
bit1..7 = reserved

在 QBV3 Binary Container 层,Parser 只验证:

flags 的 reserved bits == 0
Descriptor 本身 structurally valid
Plane bytes / semantic / dtype / validity / packing 等满足本文 Binary Contract

QBV3 Binary Container 不根据天气 Profile 名称决定 required Plane 集合。

因此必须区分:

Binary-valid

和:

Profile-valid

9.1 Binary-valid

只由:

QbinV3Decoder

根据本文 normative Binary Contract 判断。

一个 QBV3 文件即使缺少某个产品 Profile 所需 Plane, 只要文件自身 Header / Descriptor / Payload / CRC / Validity / Rice 等合法, 仍可以是:

binary-valid

9.2 Profile-valid

具体:

wind / 2
wave / 2
rain / 2
current / 2

要求哪些 Plane、dtype、scale、offset、unit、group/component, 属于外部 Profile Contract / Registry。

例如一个 binary-valid 文件:

VECTOR_U
VALIDITY

拿 wind / 2 Contract 验证时因为缺失 VECTOR_V:

Profile validation → reject

但不能称为:

QBV3 corruption

9.3 REQUIRED flag 的职责

Descriptor REQUIRED flag 是:

文件内 requirement declaration / mirror

外部 Profile Contract 可以要求:

required Plane 必须存在
对应 Descriptor REQUIRED bit 必须一致

这种一致性属于:

Encoded Field Ingress / Profile Contract Validation

而不是 QBV3 binary parser 对天气业务完整性的判断。

不得在 QbinV3Decoder 内写:

if fieldType == WIND:
    require U
    require V

也不得让 Binary Decoder 依赖:

NavSea
React
Contract JSON
fieldProfileId
fieldProfileVersion

9.4 QBV3 Quantization Contract 的边界

QBV3 Binary Container 自身承载:

dtype
scale
offset

Parser 必须验证它们满足本文通用 Binary Contract:

dtype 合法
scale / offset finite
canonical float / +0.0
Direction Circular Contract
Validity fixed descriptor

具体 Profile 是否要求:

INT16
scale = 0.1
offset = +0.0

由外部 Profile Contract 决定。

因此:

同一个合法 QBV3 的 byte-level 解释不能依赖 Profile 名称或外部 Contract。

10. Quantization 与编码必须分离

V3 强制区分:

Physical Quantization

和:

Binary Entropy Coding

物理误差只允许来自 Quantization:

physical → quantized integer

以下步骤必须完全可逆:

2D Predictor
ZigZag
Rice

因此:

V3 decode 后的 quantized integer
MUST byte/number-exact 等于 Writer Quantizer 的输出整数

不得在 Rice/Predictor 阶段引入第二次近似。


11. Generic Numeric Quantization

普通非循环 Numeric Plane:

physical = raw × scale + offset

11.1 Quantizer 参数以 Descriptor binary32 为唯一来源

Writer 必须先确定最终要写入 Descriptor 的:

scaleBits  : IEEE-754 binary32
offsetBits : IEEE-754 binary32

然后:

scaleF32  = binary32_from_bits(scaleBits)
offsetF32 = binary32_from_bits(offsetBits)

实际 Quantizer 计算必须使用已经序列化确定的 binary32 值,必要时提升为 binary64:

scaleQ  = double(scaleF32)
offsetQ = double(offsetF32)

normalized = (physical - offsetQ) / scaleQ
raw = round_half_away_from_zero(normalized)

禁止:

用配置文件中的 decimal 原值
用序列化前更高精度的 double scale / offset
量化完成后再把 scale / offset 截成 float32

原因:

QBV3 的 Quantization 参数必须与文件 Descriptor 中实际携带的 bit-pattern 完全一致。

在“相同 canonical physical input + 相同 Descriptor bit-pattern”的前提下,Writer 必须产生相同 quantized integer。

普通 Writer:

normalized = (physical - offsetQ) / scaleQ
raw = round_half_away_from_zero(normalized)

Validity 必须在 Numeric Quantizer 之前已经确定。

对每个 cell:

if validity[index] == 1:
    physical MUST be finite IEEE-754 binary64
    执行 Numeric / Direction Quantization

if validity[index] == 0:
    physical value is ignored
    MAY be NaN / +Infinity / -Infinity / missing
    MUST NOT 进入 Quantizer
    MUST NOT 产生 Rice symbol
    decoded canonical raw MUST = 0

禁止:

通过 physical 是 NaN / Inf 自动推导 validity = 0

Validity 是独立、权威的 Shared Validity Domain; non-finite numeric payload 不能替代 Validity BITSET。

round_half_away_from_zero(x) 的数学定义冻结为:

if x >= 0:
    result = floor(x + 0.5)
else:
    result = ceil(x - 0.5)

Quantizer 的规范计算域:

physical : IEEE-754 binary64
scaleQ   : exact binary64(double(scaleF32))
offsetQ  : exact binary64(double(offsetF32))

规范计算步骤:

delta64 =
    roundTiesToEven_binary64(
        physical - offsetQ
    )

normalized64 =
    roundTiesToEven_binary64(
        delta64 / scaleQ
    )

delta64 MUST finite
normalized64 MUST finite

如果任一 intermediate 为:

```text
NaN
+Infinity
-Infinity

Writer MUST FAIL。

raw = round_half_away_from_zero(normalized64)


这里的:

```text
roundTiesToEven_binary64

表示 IEEE-754 binary64 round-to-nearest, ties-to-even。

Writer 必须得到与上述步骤相同的 binary64 结果,不得依赖:

host locale
decimal string formatting
host default integer cast rounding
FPU 当前 rounding mode
excess precision / extended register precision

来决定最终 raw integer。

超出逻辑 dtype:

Writer FAIL

禁止:

silent clamp
integer wrap
隐式 modulo

Invalid Cell 不进入 Numeric Rice symbol stream;Decoder materialize 时其 raw 值必须恢复为 0。


12. V3 第一版支持的 Numeric dtype

第一版编码支持:

INT16
UINT16
INT8
UINT8

所有上述 dtype 在进入 Predictor 前转换到至少 int32 运算域。

Predictor residual:

int32

ZigZag 输出:

uint32

FLOAT32 不进入 V3 第一版 Rice Numeric Plane。

需要 FLOAT32 的 Field:

继续使用其他已支持 Container
或未来明确扩展 V3 codec contract

不得私自把 FLOAT32 bytes 当整数 Rice 编码。


13. Shared Validity

V3 第一版继续保留 v4.4 的单 Frame Shared Validity Domain:

semantic = VALIDITY
valueClass = VALIDITY
dtype = BITSET
unit = NONE
groupId = 0
component = NONE
flags = REQUIRED

BITSET:

LSB first
byteLength = ceil(count / 8)

最后 byte 未使用 high bits:

MUST = 0

Validity bit 与 flattened cell index 的对应关系冻结为:

index = row * width + col

byteIndex = index >> 3
bitIndex  = index & 7

valid =
    (validityBytes[byteIndex] >> bitIndex) & 1

因此每个 byte 内:

bit 0 → 较小 index
bit 7 → 较大 index

即明确的 LSB-first。

一个 Frame 内:

所有 Numeric Plane MUST 共享完全相同 validity domain

不同 validity domain 的数据继续拆成不同 Field/QBIN。

V3 第一版不增加:

ALL_VALID special payload
ALL_INVALID special payload
RLE validity
per-plane validity
per-group validity

原因:保持 Container 简单统一。

Distribution 层 gzip 可以自然压缩全 1 或高度重复的 BITSET。

13.1 Validity 的逻辑解析顺序

Descriptor Table 仍按本文 canonical descriptor order 排序,Payload 仍按 Descriptor 顺序打包。

QBV3 不要求 Validity Plane 在物理 Payload 中排在 Numeric Plane 前面。

Decoder 必须采用两阶段逻辑解析:

1. 解析全部 Header / Descriptor
2. 定位唯一 Shared Validity Plane
3. 完成 CRC / padding / Validity BITSET 校验
4. 读取完整 Validity BITSET
5. 计算每个 Rice block 的 valid symbolCount
6. 再解各 Numeric Plane

因此:

逻辑上 Validity 必须先可用
!=
物理上 Validity 必须排在最前

不得为了 Rice 解码方便而改变 Descriptor canonical order 或 Plane packing order。


14. 2D Spatial Predictor

V3 第一版所有 Numeric Plane 使用同一个二维 predictor。

预测在整个 Grid 的全局坐标上进行,不因 Rice block 边界 reset。

对当前 cell (r,c):

L  = (r, c-1)
U  = (r-1, c)
UL = (r-1, c-1)

预测规则:

如果 current valid 且 L/U/UL 全部 valid:
    predicted = raw(L) + raw(U) - raw(UL)

否则如果 current valid 且 L valid:
    predicted = raw(L)

否则如果 current valid 且 U valid:
    predicted = raw(U)

否则:
    predicted = 0

普通 Numeric residual:

residual = raw(current) - predicted

所有中间计算至少使用:

int32

不得用 int16 中间运算导致 overflow。

Invalid cell:

不产生 Rice symbol
Decoder materialize raw = 0

15. Direction Circular Quantization / Predictor

DIRECTION_ANGLE 继续使用 v4.4 的物理约定:

DIRECTION_TO
0°   True North
90°  East
180° South
270° West
clockwise positive
[0°, 360°)

QBIN V3 不再把 360 / 65536° 作为唯一合法 Direction scale。 Direction quantization 正式升级为通用 Circular Contract。

Descriptor 基本约束:

valueClass = DIRECTION_ANGLE
dtype = UINT16
unit = DEGREE
component = NONE
offset = 0
scale > 0
scale finite

15.0 QBV3 Direction Validation Scope

本节只定义:

QBV3 / version=3

自身的 Direction Circular Contract。

QBV3 Parser 对 DIRECTION_ANGLE 必须验证:

dtype = UINT16
unit = DEGREE
component = NONE
groupId = 0
offset = canonical +0.0f
scale = canonical float32(360/N)
rawDirection ∈ [0,N)

QBV2 的 legacy acceptance、共享 Validator 如何重构等兼容实现问题:

不属于 QBV3 Binary ABI

移至 Appendix A。

15.1 Canonical Direction Scale

Direction scale 不使用实现方自行决定的 epsilon / tolerance。 协议直接冻结 canonical float32 计算规则。

Reader / Writer 均定义:

scaleF32 = Descriptor 中 scale 的 IEEE-754 float32 值

ratioToN = 360.0 / double(scaleF32)

在把 ratioToN 转为整数前必须先验证:

ratioToN finite
1 <= ratioToN <= 65536

然后固定:

N = floor(ratioToN + 0.5)

由于 ratioToN > 0,该规则等价于正数域的 round_half_away_from_zero,不依赖 host FPU rounding mode。

必须满足:

1 <= N <= 65536

ratio64 = 360.0 / double(N)
ratio64 finite

canonicalScaleF32 =
IEEE-754 binary32(
    ratio64,
    round-to-nearest,
    ties-to-even
)

bit_pattern(scaleF32) == bit_pattern(canonicalScaleF32)

即:Descriptor 中的 scale 必须是 360/N 转为 IEEE-754 float32 后的唯一 canonical bit pattern。 不得接受“足够接近”的任意浮点值。

因此不再需要 DIRECTION_SCALE_TOLERANCE。

逻辑 raw 范围:

rawDirection ∈ [0, N)

合法 Profile 示例:

N = 65536 → scale = float32(360/65536)°
N = 360   → scale = 1°
N = 720   → scale = 0.5°

QBV3 Writer 的 Direction input 必须在进入 Writer 前已经是 canonical:

physicalDirection finite
0 <= physicalDirection < 360
DIRECTION_TO
clockwise positive
True North = 0°

QBV3 Writer 不负责:

FROM → TO 转换
任意角度 wrap
天气 Provider 方向约定修正

这些属于上游 Normalizer。

Writer 使用:

scaleQ = double(scaleF32)

normalizedDirection64 =
    roundTiesToEven_binary64(
        physicalDirection / scaleQ
    )

normalizedDirection64 MUST finite
否则 Writer MUST FAIL。

rawDirection =
    round_half_away_from_zero(normalizedDirection64) mod N

其中 mod N 只用于处理接近 360° 边界时量化得到 N 的合法 wrap, 输入 physicalDirection 本身仍必须位于 [0,360)。

QBV3 Binary Parser 只验证上述通用 Circular Contract,禁止:

if profile == wave:
    allow 1 degree

合法性来自 scale → N → canonical float32(360/N), 不得把具体天气 Profile 名称写进 Container Parser。

15.2 Direction Integer Normalization

所有 Direction predictor / residual 运算必须使用整数圆周语义,不得依赖不同语言对负数 % 的行为。

定义 normalize_direction(x, N),其结果必须位于 [0, N)。 对于本协议 predictor 产生的范围,固定参考规则为:

if x < 0:
    x = x + N
else if x >= N:
    x = x - N
return x

该规则成立是因为输入 raw 均在 [0,N),三邻居 predictor L + U - UL 的结果必定位于:

[-(N-1), 2(N-1)]

因此最多需要一次 +N 或 -N。

如果实现使用通用 modulo helper,也必须保证得到相同的 non-negative modulo 结果。

15.3 Circular Predictor

空间邻居选择和 fallback 顺序继续使用第 14 节 validity-aware 规则。

当 L/U/UL 全部 valid 时,Direction predictor 必须先计算 widened integer:

p = int32(raw(L)) + int32(raw(U)) - int32(raw(UL))
predictedRaw = normalize_direction(p, N)

Fallback:

L valid → predictedRaw = raw(L)
U valid → predictedRaw = raw(U)
否则    → predictedRaw = 0

不得把未经 mod N 规范化的 L + U - UL 直接用于 Direction residual。

15.4 circular_shortest_delta() 精确公式

设:

d = int32(currentRaw) - int32(predictedRaw)

先将差值规范到 [0,N):

m = d
if m < 0:
    m = m + N

此时 m ∈ [0,N)。

然后固定:

if 2*m > N:
    residual = m - N
else if 2*m < N:
    residual = m
else:
    # 仅 N 为偶数且恰好半圈时成立
    residual = -(N / 2)

因此 half-circle tie 固定选择负方向 -N/2。

该选择属于 QBV3 Version 3 ABI,不得由 Writer / Decoder 自行选择 +N/2。

Residual 范围:

N 为偶数:[-N/2, N/2)
N 为奇数:[-floor(N/2), floor(N/2)]

Decoder:

x = int32(predictedRaw) + residual
rawDirection = normalize_direction(x, N)

边界 Golden Test 至少覆盖:

359° → 0°
0° → 359°
N-1 → 0
0 → N-1
偶数 N 的 +N/2 / -N/2 tie → 固定解为 -N/2
三邻居 L+U-UL < 0
三邻居 L+U-UL >= N

在 scale = 1° 时,359° → 0° 必须得到最短圆周残差,而不是 ±359。


16. ZigZag 与固定宽度整数语义

Signed residual 使用 32-bit 逻辑 ZigZag。

精确数学映射冻结为:

zigzag(r):

if r >= 0:
    z = 2 * r
else:
    z = -2 * r - 1

inverse:

unzigzag(z):

if (z & 1) == 0:
    r = z / 2
else:
    r = -(z / 2) - 1

示例:

0  → 0
-1 → 1
1  → 2
-2 → 3
2  → 4
...

文档中的位运算表达只用于说明数学关系,不得直接要求 C++ 对 signed integer 执行可能产生 UB / implementation-defined 行为的 shift。

正式 Writer / Decoder 必须冻结:

residual / predictor arithmetic:至少 int32
ZigZag 数学计算:使用 int64 / uint64 widened domain
最终 ZigZag symbol:验证后收敛为 uint32
bit manipulation:uint32 / uint64
shift count:先验证再执行
add / sub / multiply:checked 或 widened arithmetic

ZigZag 与 inverse ZigZag 必须提供 Android / iOS 共用的固定宽度 C++ reference implementation, 不得依赖:

signed left shift overflow
signed right shift implementation behavior
host integer width
compiler bitfield layout

Rice remainder mask 也必须使用明确的 unsigned 宽度,例如逻辑上:

uint64_t{1} << k

并保证:

k = 0..31

时不会发生 signed 1 << 31 或非法 shift。

Golden Test 至少覆盖:

0
1
-1
INT16_MIN
INT16_MAX
最大合法 predictor residual

Predictor、ZigZag、Rice 必须完全可逆,不能引入任何新的数值误差。


17. Rice Block Geometry:冻结前候选与最终 ABI

V3 已确定采用:

validity-aware 2D Predictor
→ ZigZag
→ block-adaptive Rice

截至文档 v2.2,Block Geometry 是 QBV3 Version 3 尚未冻结的唯一核心 Binary ABI 参数。

因此:

v2.2 仍是 freeze candidate
不是 production-final ABI

在 16×16 / 32×32 benchmark 完成并回填唯一尺寸前, 任何实验文件都不得作为长期可解释的 production QBV3/version=3 发布。

但在正式发布 QBV3 / version=3 前,Block Geometry 尚需用真实 C++ Decoder 做最后一次 16×16 / 32×32 对比。

当前候选:

Candidate A:16×16
Candidate B:32×32

原因:现有 Wind 143 帧实验中,两者编码体积差异约只有:

Rice16 ≈ 43.929 MiB
Rice32 ≈ 43.953 MiB
差约 25 KiB / 143 帧
≈ 175 B / Frame
≈ 0.056%

而 32×32 的 block 数约为 16×16 的 1/4,可能减少:

block metadata
loop / branch 次数
per-block k 搜索次数

因此正式冻结前必须在同一 C++ prototype 中记录:

encoded bytes
decode ms
encode ms
peak temporary bytes
block count

分别比较四类真实数据:

Wind
Rain
Wave
Current

17.0 Block Partition 数学定义

在 Block Geometry 最终冻结前,用参数:

BLOCK_WIDTH
BLOCK_HEIGHT

表示候选尺寸。

定义:

blockCols =
    (width + BLOCK_WIDTH - 1) / BLOCK_WIDTH
    # integer floor division

blockRows =
    (height + BLOCK_HEIGHT - 1) / BLOCK_HEIGHT

对:

br ∈ [0, blockRows)
bc ∈ [0, blockCols)

定义:

rowBegin = br * BLOCK_HEIGHT
rowEnd   = min(rowBegin + BLOCK_HEIGHT, height)

colBegin = bc * BLOCK_WIDTH
colEnd   = min(colBegin + BLOCK_WIDTH, width)

Block traversal:

for br = 0 .. blockRows-1:
    for bc = 0 .. blockCols-1:
        process block(br, bc)

等价:

blockIndex = br * blockCols + bc

Block 内:

for row = rowBegin .. rowEnd-1:
    for col = colBegin .. colEnd-1:
        index = row * width + col

        if validity[index] == 1:
            emit one Rice symbol
        else:
            emit no symbol

边缘 block 自然裁切到实际 Grid 边界,不补虚拟 cell。

Predictor 继续使用全局 Grid 邻居, 不得因为进入新 block 而 reset。

17.1 正式发布规则

QBV3 第一版不增加 blockSize 字段,也不增加 codec enum。

这意味着:一旦 benchmark 选择最终 Block Geometry 并正式发布:

magic = QBV3
version = 3

选定的 block width / height 就成为 Container V3 ABI 的固定组成部分。

在正式冻结前,测试 Writer / Decoder 可以通过编译参数或测试参数运行 16×16 / 32×32; 测试参数不得写进正式 QBV3 文件,也不得形成两种 production version=3 文件解释。

Block Geometry 未冻结期间产生的实验文件:

不得进入 production Manifest
不得进入正式 CDN / qbin_v3 发布路径
不得被 NavSea production 配置发现或缓存
不得标记为正式 QBV3 Golden Fixture

实验产物必须位于明确的:

/tmp
_runtime
experimental/
或其他非 production 路径

只有 Block Geometry 选定、文档回填唯一尺寸并完成对应 Writer/Decoder round-trip 后,才允许启用 production QBIN_V3 / container_version=3 Manifest。

边缘 block 可以小于正式 block width / height。

Block 顺序:

north → south
west → east

即 block row-major。

Block 内 symbol 顺序:

cell row-major
仅遍历 validity=1 的 cell

如果 block 中没有 valid cell:

该 block 写 0 bytes

如果至少存在一个 valid cell:

1 byte riceK
随后为 Rice bitstream
最后补 0 bit 到 byte boundary

riceK:

uint8
允许范围 0..31

Writer 必须选择使该 block Rice bit 数最小的 k。

Tie:

选择较小 k

保证 deterministic output。


18. Rice Bitstream 精确定义

每个 ZigZag value z:

q = z >> k
r = z & ((1u << k) - 1)     # 数学表达;实现使用安全 unsigned width

编码:

1. 写 q 个 0 bit
2. 写 1 个终止 bit
3. 写 k 个 remainder bit

remainder bit 顺序:

Least Significant Bit first

整个 block bitstream 也采用:

LSB-first within each byte

block 最后不足 8 bit:

high unused bits MUST = 0

Decoder 根据 validity mask 可以提前知道该 block 的 symbolCount。

Decoder 必须:

精确解出 symbolCount 个 symbol
然后到达/验证该 block 的 byte boundary

不得依赖 sentinel。

不得读过 Descriptor.byteLength。

在 unary quotient 扫描过程中,Decoder 必须在每次 bit advance 前检查剩余 encoded range; 以下情况全部 fail-closed:

终止 bit 在 Plane 结束前不存在
q 超过可安全重建 uint32 residual 的范围
q << k / q * 2^k 发生溢出
remainder 不完整
非零 block padding bit
symbolCount 不足或超出

18.1 Logical Residual Upper Bound

除 bitstream / uint32 overflow 检查外,Decoder 还必须根据逻辑 dtype 拒绝数学上不可能出现的 residual,避免 CRC 合法但恶意构造的超长 unary quotient 消耗无意义 CPU。

对普通非 Direction Numeric Plane,设 logical raw dtype 的最小/最大值为:

minRaw
maxRaw

在第 14 节 predictor 下,最大可能 residual 绝对值为:

MAX_ABS_RESIDUAL = 2 × (maxRaw - minRaw)

因此:

INT16 / UINT16 → MAX_ABS_RESIDUAL = 131070
INT8  / UINT8  → MAX_ABS_RESIDUAL = 510

对 Direction:

MAX_ABS_RESIDUAL = floor(N / 2)

并遵守第 15 节 half-circle tie 固定负方向规则。

Decoder 可据此得到安全上界:

MAX_ZIGZAG <= 2 × MAX_ABS_RESIDUAL
MAX_Q = MAX_ZIGZAG >> k

在扫描 unary zeros 时,一旦:

q > MAX_Q

即可立即 reject,不必继续扫描到 Plane 尾部。

完成 inverse ZigZag 后仍必须再次验证:

abs(residual) <= MAX_ABS_RESIDUAL

然后再执行 predictor reconstruction 与最终 logical raw dtype range 检查。

这些检查属于 parser hardening,不改变任何合法 QBV3 bitstream。


18.2 Rice k 的 Deterministic 选择公式

对一个 non-empty block,设其按 block 内 canonical cell order 得到的 ZigZag symbol 为:

z_0, z_1, ... z_(n-1)

对每个候选:

k ∈ [0, 31]

定义纯 Rice symbol bit count:

bits(k) =
Σ_i [ (z_i >> k) + 1 + k ]

其中:

(z_i >> k)   = unary zero count q
+1           = unary terminator bit
+k           = remainder bits

Writer 必须选择:

k* = argmin bits(k)

如果多个 k 得到相同 bits(k):

选择数值较小的 k

该 k* 属于 Deterministic Writer Contract:

Writer MUST 写入 k*

但 QBV3 Decoder 的 Binary acceptance 不要求重新计算全部 32 个候选 k。

Decoder 对每个 non-empty block 只要求:

0 <= riceK <= 31
bitstream 可在 descriptor.byteLength 内完整解出
symbolCount 精确
padding 合法
residual / reconstructed raw 合法

因此:

riceK != k*

如果其他 Binary Contract 全部合法:

MUST NOT solely cause Decoder reject

也就是说:

最优 k*
= canonical deterministic Writer output

非最优但合法的 riceK
!= QBV3 binary corruption

Decoder MUST NOT 为 canonicality 验证而强制执行:

对已解出的每个 block 再扫描 k=0..31 重算最优 k*

优化目标只计算 Rice symbol bits。

以下固定开销不参与 k 比较:

1-byte riceK
block 尾部补到 byte boundary 的 zero padding

原因是必须冻结与当前编码实验一致的 deterministic 选择语义,避免不同 Writer 分别按:

raw bit count

或:

ceil(bit count / 8)

选择出不同 k。

选定 k* 后,该 non-empty block 的 encoded byte 数:

blockByteLength =
1
+
ceil(bits(k*) / 8)

其中前 1 byte 为 riceK。

19. Numeric Plane Payload

一个 Numeric Plane payload:

Block(0,0)
Block(0,1)
...
Block(last)

Block Geometry 使用第 17 节在正式 ABI freeze 前选定的唯一尺寸。 正式 QBV3 不增加独立 block index table。

原因:

Grid size 已知
Block size 在 Container V3 ABI 中固定
Validity 已知
每个非空 block 自带 1-byte k
Decoder 可以按 symbolCount 顺序解析

V3 第一版优先完整 Frame decode,不为随机访问单个 block 增加额外索引成本。

19.0 Numeric Plane byteLength 的精确定义

对 Numeric Plane:

Descriptor.byteLength

严格定义为该 Plane 自身所有 block encoded bytes 的总和。

对每个 block:

validCount == 0
→ block contributes 0 bytes

validCount > 0
→ block contributes:
   1 byte riceK
 + ceil(RiceSymbolBits / 8)

因此:

NumericPlane.byteLength =
Σ(non-empty blockByteLength)

byteLength 不包含:

Plane 之前的 align16 padding
Plane 之后的 align16 padding
Descriptor bytes
Header bytes
其他 Plane bytes
HTTP / gzip 外层编码

Block 内最后一个 byte 的未使用 high bits仍必须为 0; 这些 bit 位属于该 block 的最后一个 encoded byte,因此属于 byteLength。

Plane 间的 16-byte alignment padding 不属于任何 Plane 的 byteLength,只由 byteOffset / payloadLength canonical packing 规则体现。

19.1 All-invalid Frame

V3 第一版正式允许:

validity 全 0

此时:

Validity Plane:
正常 BITSET,全 0

Numeric Plane:
byteLength = 0

Decoder:

不读取 Rice block
直接 materialize 全 0 canonical raw Plane

成立条件必须严格为:

validCount == 0

如果:

validCount > 0
&& numeric byteLength == 0

必须 reject。

all-invalid:

不是 corruption
不需要特殊 codec
不写虚假的 1-byte block

产品层如果不希望发布 all-invalid Frame,可以在 Weather Writer / Product Validation 拒绝, 但 Container 行为必须保持上述确定语义。


20. Plane Packing / Alignment

Descriptor Table 排序继续继承 v4.4:

semantic
→ groupId
→ component

Plane payload 按 Descriptor 顺序排列。

plane[0].byteOffset = 0
plane[n].byteOffset = align16(previousEnd)
payloadLength = align16(lastEnd)

其中:

previousEnd = previous.byteOffset + previous.byteLength
align16(x) = (x + 15) & ~15

Plane 间与尾部 padding:

MUST = 0

区别仅在:

Numeric Plane byteLength
= Rice encoded bytes

Validity:

byteLength = ceil(count/8)

V3 仍要求 canonical minimal packing,不允许额外预留空间。


21. CRC32

QBV3 使用:

CRC-32 / ISO-HDLC

完整参数冻结为:

width   = 32
poly    = 0x04C11DB7
init    = 0xFFFFFFFF
refin   = true
refout  = true
xorout  = 0xFFFFFFFF
check("123456789") = 0xCBF43926

对 reflected 实现,等价使用:

reflected polynomial = 0xEDB88320

与:

zlib.crc32

兼容。

CRC 覆盖完整未经 HTTP Distribution Encoding 的 QBV3 bytes:

Header
Descriptor Table
Payload
Plane padding
Tail padding

计算时:

Header offset 76..79
fileCrc32 的 4 bytes
按 0x00 0x00 0x00 0x00 参与计算

Writer:

1. 先写 fileCrc32 = 0
2. 对完整 QBV3 bytes 计算 CRC
3. 将 uint32 CRC little-endian 写回 offset 76..79

Parser:

读取 stored CRC
↓
计算时把 offset 76..79 逻辑视为 0
↓
比较 uint32

不得对 gzip / HTTP transport 后的 bytes 计算 QBV3 CRC。

如果同时 benchmark QBV2 / QBV3, CRC helper 必须一致,并单独记录 crcMs。

22. Parser 安全规则:Encoded Budget 与 Decoded Budget 分离

QBV3 Version 3 的 parser conformance limits 冻结为:

MAX_PLANE_COUNT = 32
MAX_CELL_COUNT = 2,097,152
MAX_QBIN_BYTES = 64 MiB
MAX_DECODED_BYTES_PER_FRAME = 32 MiB

其中:

64 MiB = 64 × 1024 × 1024 bytes
32 MiB = 32 × 1024 × 1024 bytes

这些值属于 QBV3 Version 3 的 normative acceptance limits。

当前 Wind / Rain / Wave / Current 生产需求确认在该容量范围内, 因此 QBV3 Version 3 继续冻结这些上限。

任何 conforming Decoder 对超过上述限制的文件:

MUST fail-closed

实现可以额外拥有更严格的本地资源 policy, 但不得把一个超出上述 QBV3 conformance limit 的文件视为 Version 3 合法文件。

未来如果业务确实需要突破任一上限:

MAX_PLANE_COUNT
MAX_CELL_COUNT
MAX_QBIN_BYTES
MAX_DECODED_BYTES_PER_FRAME

不得在已发布 QBV3/version=3 上静默放宽后仍声称 ABI 未改变。

应通过:

新的 Container Version
或明确 successor binary specification

升级并重新冻结对应 conformance limits。

QBIN V3 的 encoded size 与 decoded size 不再近似相等,因此必须分别限制,并冻结 Parser 执行顺序。

22.1 Stage A:便宜的结构与 Per-frame Decoded Budget 预检查

在 CRC 与任何 Rice entropy decode 之前,先执行不需要解释 Rice bitstream 的结构检查:

magic == QBV3
version == 3

planeCount > 0
planeCount <= MAX_PLANE_COUNT

width >= 2
height >= 2
cells = checked(width × height)
cells <= MAX_CELL_COUNT

payloadLength 合理
actualFileLength == 112 + planeCount×32 + payloadLength
actualFileLength <= MAX_QBIN_BYTES

reserved == 0
Header 固定 ABI 值合法
bounds / time 合法

Descriptor order canonical
(semantic, groupId) unique
count == cells
dtype / valueClass / component 合法
scale / offset 满足 canonical float 规则
byteOffset / byteLength 范围合法
canonical align16 packing / payloadLength 公式合法

同时执行 Encoded Budget:

actual file bytes
payloadLength
每个 Plane encoded byteLength
所有 offset + length checked arithmetic

并在任何大 Plane allocation 前执行 Decoder 自己负责的 per-frame decoded budget:

decodedNumericPlaneBytes =
    checked(count × logical numeric dtype width)

decodedNumericBytes =
    checked(sum(decodedNumericPlaneBytes))

decodedValidityBytes =
    ceil(count / 8)

decodedFrameBytes =
    checked(decodedNumericBytes + decodedValidityBytes)

同一个 canonical Validity BITSET 只计一次。

必须满足:

decodedFrameBytes <= MAX_DECODED_BYTES_PER_FRAME

否则:

fail-closed

QBV3 Binary ABI 只冻结“必须有 per-frame decoded allocation budget 与 checked arithmetic”这一安全要求。

跨多个 retained Frame 的 Store memory policy:

不属于 QBV3 Binary Container

移至 Appendix A 的 Non-normative Integration Notes。

Stage A 不扫描 Rice unary,不根据 Rice 内容做 entropy decode。

22.2 Stage B:CRC32

Stage A 通过后,先验证完整 QBV3 CRC32。

结构 / length / bounds / budget
↓
CRC32

CRC 失败:

立即 fail-closed

不得先对 CRC 已损坏文件执行 Rice entropy decode。

22.3 Stage C:Canonical Payload / Validity

CRC 通过后再验证 Payload 内容级规则:

Plane / tail padding byte == 0
Validity dtype == BITSET
Validity byteLength == ceil(count/8)
Validity tail unused high bits == 0

随后计算:

validCount

并冻结 all-invalid 对 Numeric Plane 的约束:

validCount == 0 → Numeric byteLength MUST == 0
validCount > 0  → Numeric byteLength MUST > 0

22.3.1 Zero-length Plane Canonical Packing

all-invalid Frame 中可能出现多个 zero-length Numeric Plane。

canonical packing 继续按:

nextOffset = align16(previousEnd)

计算。

因此:

zero-length Plane MAY share byteOffset
with adjacent zero-length Plane
or the next non-empty Plane.

合法示例:

Plane A  offset=0  length=0
Plane B  offset=0  length=0
Plane C  offset=0  length=validityBytes

这属于合法 canonical representation,不是真实 payload overlap。

Parser 的 overlap validation MUST:

只对实际占用非零 byte range 的 Plane 做 overlap 判断;
zero-length interval 不得被误判为 overlapping payload corruption。

22.4 Stage D:Rice Entropy Decode

只有 Stage A/B/C 全部通过后,才进入 Numeric Rice decode。

逐 block / symbol 验证:

riceK <= 31
非最优 riceK 本身不属于 corruption,不要求 Decoder 重算 k*
block exact symbolCount
unary quotient <= 第 18.1 节 logical MAX_Q
bitstream 不越界
q / remainder / ZigZag arithmetic 不溢出
inverse ZigZag residual 在逻辑上限内
predictor reconstruction checked arithmetic

普通 Numeric:
decoded raw 必须落入 logical dtype range

Direction:
decoded raw 必须同时满足:
0 <= rawDirection < N

否则:
fail-closed

block padding bit == 0
Plane 解码后精确消费 descriptor.byteLength

Malformed Rice stream:

fail-closed

Direction corruption / acceptance 必须额外覆盖:

decoded rawDirection >= N
→ reject

UINT16 dtype range 合法但超出当前 Circular Contract [0,N) 的值不得被接受。

22.5 固定执行顺序

正式 Parser 顺序冻结为:

Header / Descriptor / file length / bounds
↓
Encoded + Decoded budget 预检查
↓
CRC32
↓
Canonical padding + Validity BITSET 检查 / validCount
↓
Rice entropy decode + ZigZag + Predictor reconstruction
↓
canonical raw materialization
↓
QBV3 Binary Decoder Accepted

之后如果存在外部 Profile Contract:

Encoded Field Ingress
↓
Profile Contract Validation
↓
Product / Runtime acceptance

该第二阶段不属于 QBV3 Binary Container validation。

实现可以合并无副作用的小检查,但不得把 Rice entropy decode 提前到 CRC 之前,也不得在 budget 预检查前进行大内存 allocation。


23. Canonical Decoded Raw Reference Model

本节不是 Runtime API 规范,而是 QBV3 Round-trip 的参考解码结果定义。

Numeric Plane:

QBV3 Rice Plane
↓
Rice decode
↓
inverse ZigZag
↓
Predictor reconstruction
↓
logical quantized integer
↓
canonical little-endian fixed-width raw bytes

Canonical raw:

INT16  → count × 2 bytes, little-endian two's-complement
UINT16 → count × 2 bytes, little-endian
UINT8  → count × 1 byte
INT8   → count × 1 byte, two's-complement

Invalid cell:

validity = 0
→ canonical decoded raw MUST = 0

Validity reference decoded form:

保持 canonical BITSET bytes
LSB-first
tail unused high bits = 0

bit-exact Round-trip 的权威比较对象:

Writer Quantizer integer grid
+
Writer Validity BITSET

与:

Decoder canonical integer grid
+
Decoder Validity BITSET

必须完全一致。

任何 Runtime-specific:

DecodedFieldFrame
ContentIdentity
hash
FieldSeries
Projection
Renderer

均不属于 QBV3 Binary ABI,移至 Appendix A。

24. 非 Normative 的 Profile / 实验 / Distribution 信息

Wind / Rain / Wave / Current 第一阶段候选 Profile、压缩实验统计、 HTTP Distribution Encoding 与 Manifest 示例都属于产品 / Profile / 发布层信息, 不属于 QBV3/version=3 Binary ABI。

统一移至 Appendix B。

QBV3 Binary Parser 不得根据:

wind / 2
wave / 2
rain / 2
current / 2
Manifest
文件扩展名
HTTP Content-Encoding

改变 Header / Descriptor / Validity / Predictor / ZigZag / Rice / Packing / CRC 的解释。

25. Container Version Dispatch 的最小要求

如果同一系统同时支持 QBV2 / QBV3:

QBV2 → magic "QBV2", version=2
QBV3 → magic "QBV3", version=3

Unknown magic/version combination:

fail-closed

除以上识别规则外,迁移、Store、Runtime、平台实现均不属于 QBV3 Binary ABI。

26. Binary Spec 与消费实现分离

QBV3 Binary Container 只规定:

bytes 如何写
bytes 如何验证
bytes 如何无损恢复 quantized integer grid

NavSea、React、Store、Binding、Renderer、平台 Bridge 等消费方式:

不属于本文 normative Binary ABI

相关背景统一放入 Appendix A。

27. Decoder 实现语言中立

QBV3 不规定 Decoder 必须使用何种语言。

任何 conforming implementation 都必须对同一合法 QBV3 bytes 得到相同:

Header
Descriptor
Validity
canonical quantized integer grid

并对同一 malformed bytes 得到相同 Binary Accept / Reject 结论。

28. Deterministic Writer 与 Container V3 Codec ABI

相同:

Canonical Field
Field Profile Version
Container Version

必须生成:

byte-for-byte identical QBV3

正式 QBV3 / version=3 发布前,必须先完成第 17 节 Block Geometry benchmark 并选择唯一尺寸。 一旦正式发布,下列内容全部成为 Container V3 Binary ABI:

validity-aware predictor 规则
predictor fallback 顺序
Direction circular residual 规则
ZigZag 定义
最终冻结的 Block Geometry
Block traversal order
Cell traversal order
valid-cell-only symbol stream
Rice k search 0..31
Rice k tie → smaller k
Rice unary form
Rice remainder bit order
byte 内 bit order
block padding
Descriptor canonical order
Plane canonical packing
Padding = 0
reserved = 0
CRC algorithm

不得依赖:

hash/map iteration order
platform endian
compiler bitfield layout
undefined signed overflow / shift behavior

如果正式发布后要改变:

Block Geometry
Predictor
Rice bitstream
bit order
新增第二种内部 codec

不能只修改文档修订号让相同 QBV3/version=3 出现两种解释。 必须:

升级新的 Container Version

或者在新的 Container Version 设计阶段明确引入可识别 codec 字段。 V3 第一版继续保持单一固定 codec,不为未来可能性提前增加 codec switch。


29. Golden Binary Fixtures

正式冻结后至少准备:

Wind
Wave
Rain
Current

四类真实数据的 Golden QBV3 Fixture。

每个 Fixture 至少记录:

QBV3 document revision
container version
magic

referenceTimeUtc
validTimeUtc
grid / bounds

Header bytes
Descriptor bytes

canonical physical baseline
quantized integer baseline
validity baseline

正式 QBV3 bytes
CRC32
SHA-256

raw QBV3 byteLength
gzip byteLength(若另外做 distribution benchmark)

per-plane encoded byteLength
blockCount
nonEmptyBlockCount
Rice k distribution

writer timing
decoder timing
peak temporary memory

Block Geometry 冻结前允许生成:

experimental/16x16/
experimental/32x32/

两组 benchmark fixture。

最终 Block Geometry 冻结后必须重新生成唯一正式 Golden Fixture 集合。

30. Round-trip 验收

最重要的 Round-trip 先验证整数 bit-exact:

Writer Quantizer output integer array
↓
QBV3 Writer
↓
QBV3 file
↓
C++ QbinV3Decoder
↓
Decoded canonical raw integer array

结果必须:

array_equal == true

不是近似比较。

同时:

validity array_equal == true
invalid cell decoded raw == 0

物理误差测试单独比较:

原始 canonical physical
vs
decoded raw × scale + offset

确保:

Predictor / ZigZag / Rice 不贡献任何额外误差
物理误差只来自 Field Profile Quantization

全量验证阶段建议覆盖全部生成 Frame,而不是只抽样。

Direction 必须包含:

359° ↔ 0°
N-1 ↔ 0
half-circle tie

All-invalid fixture 必须验证:

Validity 全 0
Numeric byteLength = 0
Decoded raw 全 0
zero-length Numeric Plane 可共享 canonical byteOffset

31. Binary Reject / Accept / Writer Validation 验收

本节按职责拆分为三组,避免把:

Binary corruption
Binary canonical-but-unusual input
Writer input validation

混为同一类。

31.1 Binary Reject Cases

以下 malformed / corruption 情况必须:

MUST fail-closed

至少覆盖:

Bad magic
Bad version
Big-endian/mixed endian

Header reserved != 0
Descriptor reserved != 0

Invalid planeCount
Invalid cell count
Overflowed width×height

Bad byteOffset
Bad byteLength
Non-canonical descriptor order

Extra padding
Nonzero padding

Bad CRC

Truncated Rice block
Rice unary never terminates before end
riceK > 31

Block symbol count insufficient
Block symbol count extra data
Nonzero block padding bits

Invalid validity tail bits

Numeric offset = -0.0f
Header bound = -0.0 binary64

Direction invalid canonical scale
QBV3 decoded rawDirection >= N

non-zero Plane actual byte-range overlap

semantic = 3 but valueClass != VALIDITY
semantic = 3 but dtype != BITSET
valueClass = VALIDITY but semantic != 3
dtype = BITSET but valueClass != VALIDITY

以上情况:

QbinV3Decoder
→ reject

31.2 Binary Accept Cases

以下情况本身不得单独导致 Binary reject:

non-optimal riceK
+ otherwise-valid block
→ MUST accept

Unknown fieldType
→ MUST accept as opaque hint

fieldType = 0
→ MUST accept as UNKNOWN / UNSPECIFIED opaque hint

all-invalid Frame
Validity 全 0
Numeric byteLength = 0
→ MUST accept

all-invalid zero-length adjacent Plane shared byteOffset
→ MUST accept as canonical packing

特别冻结:

riceK != Writer 最优 k*

如果其余:

riceK range
symbolCount
padding
residual
raw range
descriptor.byteLength

全部合法,则:

MUST NOT solely cause Decoder reject

Decoder 不要求为 canonicality 重新计算:

k = 0..31

的全部候选。

31.3 Writer Validation Cases

以下属于 Writer input / deterministic output 验证, 不是“Decoder corruption”:

validity=1
+ physical = NaN / +Inf / -Inf
→ Writer MUST reject

validity=0
+ numeric physical = NaN / +Inf / -Inf / missing
→ Writer MAY accept
→ value MUST be ignored
→ MUST NOT enter Quantizer
→ MUST NOT emit Rice symbol

Writer MUST NOT:
通过 NaN / Inf 自动推导 validity=0

delta64 non-finite
→ Writer MUST reject

normalized64 non-finite
→ Writer MUST reject

normalizedDirection64 non-finite
→ Writer MUST reject

physicalDirection 不满足:
finite
0 <= angle < 360
DIRECTION_TO
→ Writer MUST reject / 上游先 normalize

Writer 对每个 non-empty block:
MUST 写 deterministic 最优 k*

31.4 外部 Profile / Integration Validation

以下属于外部 Integration / Profile Contract validation, 不得标记为 QBV3 binary corruption:

Missing Profile-required Plane
Undeclared REQUIRED relative to external Contract
Contract mismatch
fieldType / fieldProfile mismatch
QBV2 Direction legacy acceptance

QBV2 的 Direction legacy regression 应在 V2 ingress compatibility test 中覆盖, 不属于 QBV3 corruption suite。

32. Codec 性能验收

QBV3 的 Binary ABI 不规定消费方 Runtime 生命周期,但正式冻结 Block Geometry 前必须完成 codec-level benchmark。

同一测试平台、同一 build 配置下至少记录:

raw QBV3 bytes
gzip QBV3 bytes(若测试 distribution gzip)

crcMs
entropyDecodeMs
totalDecodeMs
encodeMs

peakTemporaryBytes
decodedFrameBytes

blockCount
nonEmptyBlockCount
Rice k distribution

必须比较:

16×16
vs
32×32

并覆盖:

Wind
Rain
Wave
Current

如果同时比较 QBV2 / QBV3:

CRC 实现必须一致
crcMs 与 entropyDecodeMs 必须分开

不要求 QBV3 entropy decode 比固定宽度 QBV2 decode 更快。

Block Geometry 的选择依据应综合:

encoded bytes
encode CPU
decode CPU
peak temporary memory
实现复杂度

而不是只看压缩率。

32.1 Streaming reconstruction 实现目标

推荐 Decoder:

compressed Plane input
↓
BitReader
↓
逐 symbol Rice decode
↓
inverse ZigZag
↓
立即 Predictor reconstruction
↓
直接写最终 canonical fixed-width raw storage

应避免为了实现方便同时建立多个 full-grid 临时数组:

residual[]
zigzagDecoded[]
predictorOutput[]
rawOutput[]

这是性能实现目标,不改变 QBV3 bitstream。

33. QBV3 第一阶段 Binary ABI 验收基线

正式冻结 / 发布 QBV3/version=3 前至少满足:

1. Header 112-byte layout、Little Endian、reserved=0 已冻结。
2. layout=1 / projection=1 / sampleOrder=1 / gridRegistration=1 已冻结。
3. tileZ=0xffff / tileX=0 / tileY=0 已冻结。
4. referenceTimeUtc / validTimeUtc 为正 int64 Unix UTC seconds。
5. Binary Parser 不强制 validTimeUtc >= referenceTimeUtc。
6. Bounds 是 finite binary64 WGS84 lon/lat,0 必须编码为 +0.0。
7. WEB_MERCATOR + NODE projected-space sample 公式有 Golden Test。
8. Descriptor 32-byte layout 与 dtype byte enum 已冻结。
9. valueClass / component / core unit / VALIDITY semantic structural invariants 已冻结。
10. Numeric dtype 仅允许 INT16/UINT16/UINT8/INT8;FLOAT32 Rice Plane reject。
11. Shared Validity 有且只有一个,semantic=3、dtype=BITSET、REQUIRED。
12. Validity scale=+1.0f、offset=+0.0f。
13. Numeric scale > 0 finite;offset finite;零 offset 使用 +0.0f。
14. Quantizer 先确定 Descriptor binary32 scale/offset,再提升到 binary64 计算。
15. validity=1 的 canonical physical sample MUST 为 finite binary64;
    validity=0 的 numeric physical value 不参与 Quantizer,可为 NaN / Inf / missing。
16. round_half_away_from_zero 精确公式有 ±0.5 边界 Golden Test。
17. Shared Validity BITSET LSB-first,tail unused high bits=0。
18. Decoder 先解析全部 Descriptor,逻辑上先定位/读取 Validity,再解 Numeric。
19. 2D Predictor 在整个 Grid 连续,不在 block 边界 reset。
20. Direction canonical float32(360/N)、N=floor(ratio+0.5)、raw∈[0,N)。
21. Direction half-circle tie 固定为 -N/2。
22. ZigZag / inverse ZigZag 使用固定宽度无 UB 语义。
23. Block partition 数学公式、block row-major、cell row-major 已冻结。
24. 16×16 / 32×32 使用同一 Writer/Decoder prototype 完成四类真实 benchmark。
25. Rice k 使用 bits(k)=Σ((z>>k)+1+k),tie 取较小 k。
26. k 优化不把 riceK byte / byte padding 纳入目标。
27. non-empty blockByteLength = 1 + ceil(RiceSymbolBits/8)。
28. Numeric Plane byteLength = Σ(non-empty blockByteLength),不含 align16 padding。
29. all-invalid:Validity 全 0、Numeric byteLength=0、decoded raw 全 0。
30. zero-length Plane shared byteOffset 不误判 overlap。
31. Plane / tail align16 padding 全 0。
32. CRC-32/ISO-HDLC 完整参数、check value 与 CRC field-zeroing 已冻结。
33. Parser 顺序:结构+budget → CRC → Validity → Rice。
34. MAX_Q / residual logical upper-bound hardening 生效。
35. malformed Rice / truncation / overflow / nonzero padding 全部 fail-closed。
36. MAX_PLANE_COUNT=32。
37. MAX_CELL_COUNT=2,097,152。
38. MAX_QBIN_BYTES=64 MiB。
39. MAX_DECODED_BYTES_PER_FRAME=32 MiB。
40. 超出上述 Version 3 conformance limits 的文件 fail-closed。
41. 全部 Numeric Plane integer bit-exact round-trip。
42. strict Validity bit-exact round-trip。
43. Direction 359°↔0° / N-1↔0 / half-circle / predictor wrap 通过。
44. Writer deterministic:相同 canonical input + 相同 Descriptor → byte-for-byte 相同 QBV3。
45. Golden Binary Fixtures 覆盖 Wind / Rain / Wave / Current。
46. Block Geometry 未冻结前实验文件不进入 production。
47. 最终选择唯一 BLOCK_WIDTH / BLOCK_HEIGHT 后重新生成正式 Golden Binary。
48. validity=1 的 physical sample 必须 finite;validity=0 的 numeric physical value 被忽略,不参与 Quantizer。
49. Writer 不得通过 NaN/Inf 自动推导 Validity。
50. ZigZag / inverse ZigZag 精确数学公式有 Golden Test。
51. Quantizer binary64 subtraction/division 使用 round-to-nearest, ties-to-even 语义。
52. Direction input 在 Writer 前已经 canonical 为 DIRECTION_TO 且 0<=angle<360。
53. unknown fieldType 不得单独导致 binary reject。
54. 任意 Numeric offset 数值为 0 时必须使用 +0.0f,-0.0f reject。
55. Validity BITSET 的 byteIndex / bitIndex 公式有 byte-exact Golden。
56. semantic==3 ⇔ valueClass==VALIDITY ⇔ dtype==BITSET 的双向规则通过 corruption test。
57. delta64 / normalized64 / normalizedDirection64 non-finite → Writer FAIL。
58. fieldType=0 作为 UNKNOWN / UNSPECIFIED opaque hint 可 binary-accept。
59. Writer 对每个 non-empty block 写最优 k*。
60. Decoder 不因非最优但 otherwise-valid 的 riceK 单独 reject,也不要求重算 k*。
61. WEB_MERCATOR mercatorX / mercatorY / inverse 数学公式有定位 Golden Test。
62. 一个 targetTime 一个独立 QBV3 Frame。

在 v2.2 中:

除最终 BLOCK_WIDTH / BLOCK_HEIGHT 尚需通过 16×16 / 32×32 C++ benchmark 实测选择外,其他核心 QBV3 Binary ABI 语义已完成文档冻结;Rice canonicality 采用 Writer-canonical / Decoder-permissive。v2.2 之后不再因实现偏好继续修改 Binary Spec。

消费侧 Store、Binding、Renderer、NavSea、React、Profile Contract、Manifest 与平台 Bridge 不作为 QBV3 Binary ABI 的冻结条件。

34. 版本与命名最终规则

以后统一使用:

QBIN V2
= Container Version 2
= Magic QBV2
= 最终权威设计基线文档 v4.4

QBIN V3
= Container Version 3
= Magic QBV3
= 本文从 Document Revision v1.0 开始;当前修订为 v2.2

禁止再使用:

“QBIN V4”

来代指 Container V2。

如果需要说明历史:

QBIN V2 / v4.4 design baseline

如果需要说明新格式:

QBIN V3

Field Profile Version 独立:

wind / 2
wave / 2
rain / 2
current / 2

35. 最终冻结候选语句

架构继承:

QBIN V3 继承 QBIN V2 / v4.4 的 Semantic、Unit、Field Profile Contract、Independent Binding、Forecast Runtime 与 Generic Renderer 架构;只在 Encoded Field Ingress 之前改变二进制 Numeric Plane 表达。

数值误差:

V3 的物理误差只来自 Field Profile Quantization;2D Predictor、ZigZag 与 Rice 必须 bit-exact 可逆。

统一编码:

V3 第一版所有整数 Numeric Plane 统一使用 validity-aware 2D Predictor + ZigZag + 单一固定 Block Geometry 的 adaptive Rice;Validity 继续使用 shared BITSET。Block Geometry 在正式 QBV3/version=3 发布前通过 16×16 / 32×32 C++ benchmark 选定,发布后成为不可兼容修改的 Container V3 ABI。

Direction:

QBV3 DIRECTION_ANGLE 使用 Generic Circular Quantization Contract;合法 scale 必须满足 canonical float32(360/N),Container Parser 不根据具体天气 Profile 名称放宽或改变该规则。

Decoded contract:

QBV3 Decoder 的 reference decoded result 是 canonical fixed-width quantized raw grid;Predictor / ZigZag / Rice 必须完全可逆,Invalid cell 的 canonical raw 固定为 0。

职责边界:

QBV3 Binary ABI 只规定 Writer 如何生成 bytes、Parser 如何验证 bytes、Decoder 如何无损恢复 quantized integer grid;消费侧架构不属于 Container 规范。

时间单位:

一个 targetTime = 一个独立 QBV3 Frame,不把多个时间点合并成一个网络归档。

四类第一阶段 Profile:

Wind    U/V 0.1 m/s
Rain    0.1 mm/h
Wave    Height 0.05 m / Period 0.1 s / Direction 1°
Current U/V 0.02 m/s

Wave Range:

Wave Height 不以 10 m 为格式上限;第一阶段至少验证 0–30 m,10 m 必须正常表达。

版本:

新编码不继续冒充 QBIN V2,也不创建“QBIN V5”第二套编号;直接使用 QBIN V3 / QBV3 / version=3。


36. 当前状态与下一步

本文当前状态:

设计冻结候选 v2.2

已经完成:

Header / Descriptor byte layout
Little Endian / canonical float
Header fixed FRAME ABI
WEB_MERCATOR + NODE Grid 空间语义
DType / ValueClass / Component / Core Unit 机器值
Shared Validity BITSET
Generic Numeric Quantization
round_half_away_from_zero
Direction Circular Contract
2D Predictor
ZigZag
Rice bitstream
Rice k deterministic 选择公式
Block partition 数学公式
Numeric Plane byteLength
Plane Packing / Alignment
All-invalid representation
CRC-32/ISO-HDLC 完整参数
Parser 执行顺序与 fail-closed
Parser conformance limits
Canonical decoded raw reference model
Deterministic Writer 规则
Golden / Round-trip 验收规则
Invalid-cell / non-finite physical Writer 语义
ZigZag / inverse ZigZag 精确数学 ABI
Quantizer binary64 rounding 语义
unknown fieldType forward-compatible Binary 行为
Validity BITSET 精确位索引
Numeric canonical +0.0 offset 全局规则
WEB_MERCATOR 精确数学公式
VALIDITY semantic 双向约束
Quantizer intermediate finite 规则
fieldType=0 UNKNOWN / UNSPECIFIED 语义
Rice Writer-canonical / Decoder-permissive 规则

尚未完成:

16×16 / 32×32 真实 Writer/Decoder benchmark
最终 BLOCK_WIDTH / BLOCK_HEIGHT freeze
正式 QBV3 Writer
正式 QBV3 Decoder
四类 full integer/validity bit-exact round-trip
正式 Golden Binary Fixtures
corruption / fail-closed 全量验证
codec encode/decode CPU + peak temporary memory benchmark

其中:

最终 BLOCK_WIDTH / BLOCK_HEIGHT

是正式发布前唯一仍需通过实测选择的核心 Binary ABI 参数。 其余 Binary 结构与数据语义在 v2.2 中不再保留未决分支。

从 v2.2 起,除非 Prototype / Golden / Corruption Test 证明现有 Binary Contract 存在真实不可实现、不可唯一解释或自相矛盾的问题,否则不得再因实现偏好修改 QBV3 Binary ABI。

在以上实现与验证完成前:

QBIN V2 / v4.4 仍是已实施历史基线
QBIN V3 v2.2 是下一代候选生产格式设计

下一步建议严格按以下顺序:

1. 以本文 v2.2 作为最终设计冻结稿,实现最小 deterministic QBV3 Writer + Decoder prototype。
2. 同一 prototype 仅以测试参数运行 16×16 / 32×32。
3. 用 Wind / Rain / Wave / Current 真实 Frame 做 integer/validity bit-exact round-trip。
4. 同时记录 raw/gzip bytes、encodeMs、crcMs、entropyDecodeMs、totalDecodeMs、peakTemporaryBytes。
5. 选择唯一 BLOCK_WIDTH / BLOCK_HEIGHT。
6. 将唯一 Block Geometry 回填本文,删除所有实验双选语义。
7. 生成正式 Golden Binary Fixtures。
8. 完成 corruption / fail-closed 全量验证。
9. 将该版本作为 QBV3/version=3 production-final Binary ABI 冻结稿。

Appendix A. Non-normative Integration / Compatibility Notes

本 Appendix 不属于 QBV3 Binary ABI。

A.1 QBV2 Legacy Direction Compatibility

当前项目若继续支持 QBV2,应保持其既有 authoritative ingress acceptance, 包括历史 Direction scale tolerance。

该规则属于:

QBV2 compatibility

不是 QBV3 Container 规则。

QBV3 只使用正文第 15 节的 canonical Circular Contract。

A.2 ContentIdentity / Runtime Hash

当前项目可为了 QBV2/QBV3 共存,让 decoded Plane 复用既有 runtime identity helper。

这些:

uint64 hash
runtime contentIdentity string

不写入 QBV3 文件,也不参与 QBV3 CRC,因此不是 Container Version 3 ABI。

A.3 Profile Contract

外部 Profile Contract 可以比 QBV3 Binary Parser 更严格,例如校验:

expectedDType
expectedScaleBits
expectedOffsetBits
required Plane 集合

这些属于:

Profile-valid

而不是:

Binary-valid

一个 binary-valid 但 profile-invalid 的 QBV3 文件不应被称为 binary corruption。

A.4 Store / Runtime / NavSea / React / Platform Bridge

以下都不是 QBV3 Binary ABI:

EncodedFieldStore
FieldSeries
T0 / T1 / mix
Binding
Renderer
NavSea 下载/缓存/预取
React / RN API
Android / iOS transport
retained-memory policy

这些实现不得反向改变正文对 QBV3/version=3 bytes 的解释。


Appendix B. Informative Profile / Product / Distribution Notes

本 Appendix 只保留当前 WeatherV2 第一阶段实验与产品背景, 用于 Writer Prototype 和 Golden Fixture 选样。

B.1 第一阶段候选 Profile

Wind / 2
    U/V INT16
    0.1 m/s
    offset +0.0

Rain / 2
    UINT16
    0.1 mm/h
    offset +0.0

Wave / 2
    Height UINT16 0.05 m
    Period UINT16 0.1 s
    Direction UINT16 1°
    offset +0.0

Current / 2
    U/V INT16
    0.02 m/s
    offset +0.0

这些 Profile 用于当前真实数据 benchmark, 但 QBV3 Parser 不得写 if WIND / WAVE / RAIN / CURRENT。

B.2 当前量化误差实验

Wind:
speed MAE ≈ 0.02461 m/s
speed p95 ≈ 0.05205 m/s
speed p99 ≈ 0.06201 m/s
speed max ≈ 0.07071 m/s

Rain:
MAE ≈ 0.0149 mm/h
max ≈ 0.0500 mm/h

Wave:
Height max additional error ≈ 0.02 m
Direction max additional error ≈ 0.5°

Current:
speed MAE ≈ 0.00646 m/s
speed max ≈ 0.0141 m/s

Predictor / ZigZag / Rice 不允许增加额外数值误差。

B.3 Block-size 既有实验

Wind 143 帧既有模型结果:

Rice16 ≈ 43.929 MiB
Rice32 ≈ 43.953 MiB

差约:
25 KiB / 143 Frames
≈ 175 B / Frame
≈ 0.056%

因此最终 Block Geometry 不能只看压缩率, 必须等待真实 Writer/Decoder CPU 与 memory benchmark。

B.4 Distribution / Manifest

QBV3 Container 与 HTTP Distribution Encoding 分离。

可以外层发布:

.qbin
.qbin.gz
HTTP Content-Encoding

但解压后必须恢复同一 QBV3 bytes。

Manifest / CDN path / Profile metadata:

不属于 QBV3 Binary ABI