docs: audit OpenCPN S-52 render equivalence
This commit is contained in:
566
docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v1.md
Normal file
566
docs/报告/CM95_S52_OpenCPN渲染流程对照审计报告_v1.md
Normal file
@@ -0,0 +1,566 @@
|
|||||||
|
# CM95 S-52 OpenCPN 渲染流程对照审计报告 v1
|
||||||
|
|
||||||
|
日期:2026-09-20
|
||||||
|
|
||||||
|
## 1. 审计目的
|
||||||
|
|
||||||
|
本报告不从最终截图差异反推代码缺陷,而是以 OpenCPN 源码的真实渲染执行流程为审计基准,逐阶段对照 CM95 当前实现。
|
||||||
|
|
||||||
|
目标是回答:
|
||||||
|
|
||||||
|
1. OpenCPN 从 CM93/S-57 对象到最终屏幕绘制,实际经过哪些步骤;
|
||||||
|
2. CM95 当前每一步是否等价;
|
||||||
|
3. Conditional Symbology Procedure(CSP)返回的完整 rule chain 是否被完整保存和执行;
|
||||||
|
4. 哪些当前缺陷可以在人工看图之前由源码/结构审计直接发现;
|
||||||
|
5. 后续应建立怎样的自动硬门槛,避免继续依赖人工发现遗漏。
|
||||||
|
|
||||||
|
## 2. 固定基线
|
||||||
|
|
||||||
|
OpenCPN 参考提交:
|
||||||
|
|
||||||
|
`f7211b4e1874c453e37a7f190aa191c55a25c32e`
|
||||||
|
|
||||||
|
主要参考源码:
|
||||||
|
|
||||||
|
- `gui/src/cm93.cpp`
|
||||||
|
- `libs/s52plib/src/s52cnsy.cpp`
|
||||||
|
- `libs/s52plib/src/s52plib.cpp`
|
||||||
|
- 同版本 chartsymbols / lookup 资源
|
||||||
|
|
||||||
|
CM95 审计时 HEAD:
|
||||||
|
|
||||||
|
`fe008ef6eb527d7980728de694a1c7bc81d38b3c`
|
||||||
|
|
||||||
|
主要对照文件:
|
||||||
|
|
||||||
|
- `src/cm95pbf/s52.py`
|
||||||
|
- `src/cm95pbf/csp.py`
|
||||||
|
- `src/cm95pbf/presentation.py`
|
||||||
|
- `tools/cm95_s52_style_compiler.py`
|
||||||
|
- `web/kyushu_compare.html`
|
||||||
|
|
||||||
|
## 3. OpenCPN 实际渲染流程
|
||||||
|
|
||||||
|
OpenCPN 的流程不是“CSP 选择一个最终符号”。
|
||||||
|
|
||||||
|
真实执行链是:
|
||||||
|
|
||||||
|
`CM93 object → CM93→S57 class normalization → S-52 Lookup → 初始 rule list → CS(...) → CSP 返回 instruction string → 再次解析为 rule list → 每条 primitive 逐条渲染`
|
||||||
|
|
||||||
|
### 3.1 CM93 → S57/S52 对象视图
|
||||||
|
|
||||||
|
`cm93.cpp` 在创建 S57Obj 时包含 CM93 类别替换和属性适配。
|
||||||
|
|
||||||
|
例如已确认:
|
||||||
|
|
||||||
|
- `ITDARE → DEPARE`
|
||||||
|
- `SPOGRD → DMPGRD`
|
||||||
|
- `FSHHAV → FSHFAC`
|
||||||
|
- `OFSPRD → CTNARE`
|
||||||
|
|
||||||
|
这一步决定后续采用哪一个 S-52 Lookup/CSP。
|
||||||
|
|
||||||
|
### 3.2 Lookup
|
||||||
|
|
||||||
|
Presentation Library Lookup 返回普通绘制指令和/或:
|
||||||
|
|
||||||
|
`CS(XXXXXXXX)`
|
||||||
|
|
||||||
|
OpenCPN `_LUP2rules()` 将 INST 解析成 Rule 链。
|
||||||
|
|
||||||
|
### 3.3 CSP
|
||||||
|
|
||||||
|
当遇到 `RUL_CND_SY` 时:
|
||||||
|
|
||||||
|
`GetAndAddCSRules() → RenderCS() → s52cnsy.cpp 对应 CSP`
|
||||||
|
|
||||||
|
CSP 返回的不是单一图标,而是完整 instruction string,例如可能同时包含:
|
||||||
|
|
||||||
|
`SY + TX + SY + SOUNDG03 + QUAPNT01`
|
||||||
|
|
||||||
|
或:
|
||||||
|
|
||||||
|
`AC + AP + LS + SY`
|
||||||
|
|
||||||
|
### 3.4 CSP 返回串再次解析
|
||||||
|
|
||||||
|
OpenCPN 将 CSP 返回串再次通过 Lookup/rule parser 转换成新的 Rule 链。
|
||||||
|
|
||||||
|
因此:
|
||||||
|
|
||||||
|
`SY(...);TX(...);SY(...);...`
|
||||||
|
|
||||||
|
不是提示信息,而是必须按顺序执行的正式渲染程序。
|
||||||
|
|
||||||
|
### 3.5 最终 primitive 执行
|
||||||
|
|
||||||
|
`DoRenderObject()` 对 Rule 链逐个 switch 执行。
|
||||||
|
|
||||||
|
OpenCPN 当前实际执行的主要 primitive 包括:
|
||||||
|
|
||||||
|
- `TX` → RenderTX
|
||||||
|
- `TE` → RenderTE
|
||||||
|
- `SY` → RenderSY
|
||||||
|
- `LS` → RenderLS / RenderGLLS
|
||||||
|
- `LC` → RenderLC
|
||||||
|
- `MP/MPS` → RenderMPS
|
||||||
|
- `CA` → RenderCARC
|
||||||
|
- `AC` → Area color renderer
|
||||||
|
- `AP` → Area pattern renderer
|
||||||
|
- `CS` → 再次进入 conditional symbology
|
||||||
|
|
||||||
|
结论:
|
||||||
|
|
||||||
|
**OpenCPN 的语义单元是“完整有序 Rule Chain”,不是一个最终 symbol code。**
|
||||||
|
|
||||||
|
## 4. CM95 当前流程
|
||||||
|
|
||||||
|
当前 CM95 有两条不同路径。
|
||||||
|
|
||||||
|
### 4.1 非 mariner-dependent CSP
|
||||||
|
|
||||||
|
`resolve_csp_hints()` 调用 `csp.py` resolver,得到 instruction string。
|
||||||
|
|
||||||
|
之后通过:
|
||||||
|
|
||||||
|
- `static_rule_hints()`
|
||||||
|
- `materialize_symbol_hints()`
|
||||||
|
- `materialize_text_hints()`
|
||||||
|
|
||||||
|
转换成 PBF scalar/JSON hints,再由 style compiler 消费。
|
||||||
|
|
||||||
|
### 4.2 mariner-dependent CSP
|
||||||
|
|
||||||
|
当前集合:
|
||||||
|
|
||||||
|
- DEPARE01 / DEPARE02
|
||||||
|
- DEPCNT02
|
||||||
|
- SOUNDG02
|
||||||
|
- OBSTRN04
|
||||||
|
- WRECKS02
|
||||||
|
|
||||||
|
在 `mariner_mode=runtime` 时,`s52.py` **不会执行完整 resolve_csp_hints()**。
|
||||||
|
|
||||||
|
而是只调用:
|
||||||
|
|
||||||
|
`materialize_mariner_runtime_facts()`
|
||||||
|
|
||||||
|
将中性业务事实写入 PBF,然后把 CSP 名保留给 runtime。
|
||||||
|
|
||||||
|
因此完整的 OpenCPN CSP rule chain **没有进入 PBF,也没有形成一个统一 Runtime Rule IR**。
|
||||||
|
|
||||||
|
Web 端随后在 JavaScript 中重新手写:
|
||||||
|
|
||||||
|
- DEPARE 颜色
|
||||||
|
- DEPCNT safety contour
|
||||||
|
- SOUNDG 显示
|
||||||
|
- OBSTRN/WRECKS symbol
|
||||||
|
- LIGHT CA screen-space 绘制
|
||||||
|
|
||||||
|
这形成了第二套决策逻辑。
|
||||||
|
|
||||||
|
## 5. 核心结构性缺陷
|
||||||
|
|
||||||
|
### 5.1 同一个 CSP 被实现两次
|
||||||
|
|
||||||
|
例如 OBSTRN04:
|
||||||
|
|
||||||
|
第一份逻辑:
|
||||||
|
|
||||||
|
`src/cm95pbf/csp.py::resolve_obstrn04_instruction()`
|
||||||
|
|
||||||
|
第二份逻辑:
|
||||||
|
|
||||||
|
`web/kyushu_compare.html::runtimeHazardSymbol()`
|
||||||
|
|
||||||
|
OpenCPN 只有一份 CSP 决策树,返回完整 Rule Chain,再由通用 renderer 执行。
|
||||||
|
|
||||||
|
当前架构则是:
|
||||||
|
|
||||||
|
`OpenCPN CSP → Python 人工翻译一次 → runtime facts → JavaScript 再人工翻译一次`
|
||||||
|
|
||||||
|
任何一层少一个分支都会产生视觉缺失。
|
||||||
|
|
||||||
|
本次“有虚线圈但没有 10.5”就是这种重复翻译导致的。
|
||||||
|
|
||||||
|
## 6. 已确认的源码级不等价
|
||||||
|
|
||||||
|
### 6.1 OBSTRN04 POINT:QUAPNT01 丢失
|
||||||
|
|
||||||
|
OpenCPN POINT 分支最终会追加:
|
||||||
|
|
||||||
|
`CSQUAPNT01(obj)`
|
||||||
|
|
||||||
|
无论是 isolated danger 还是普通 obstruction branch,都会执行 positional-quality presentation。
|
||||||
|
|
||||||
|
当前 `resolve_obstrn04_instruction()`:
|
||||||
|
|
||||||
|
- isolated danger:直接 `return udw`
|
||||||
|
- 普通 POINT:返回 symbol / SOUNDG03
|
||||||
|
|
||||||
|
没有追加 QUAPNT01。
|
||||||
|
|
||||||
|
因此即使 Python 固定 profile 下直接使用 resolver,也不是完整 OpenCPN rule chain。
|
||||||
|
|
||||||
|
### 6.2 OBSTRN04:OBJNAM TX 丢失
|
||||||
|
|
||||||
|
OpenCPN OBSTRN04 在统一 end 阶段:
|
||||||
|
|
||||||
|
如果存在 `OBJNAM`,追加:
|
||||||
|
|
||||||
|
`TX(OBJNAM,1,2,3,'15118',-1,-1,CHBLK,26)`
|
||||||
|
|
||||||
|
当前 Python resolver 没有该 TX。
|
||||||
|
|
||||||
|
当前 Web runtime 同样没有实现。
|
||||||
|
|
||||||
|
### 6.3 OBSTRN04 LINE:sounding 丢失
|
||||||
|
|
||||||
|
OpenCPN:
|
||||||
|
|
||||||
|
当 LINE 有 VALSOU 且 `VALSOU <= 20`,在 LS 后还会追加 `SNDFRM02/SOUNDG03` sounding presentation。
|
||||||
|
|
||||||
|
当前 Python LINE branch 只返回:
|
||||||
|
|
||||||
|
- LC
|
||||||
|
- 或 LS
|
||||||
|
|
||||||
|
没有 sounding。
|
||||||
|
|
||||||
|
### 6.4 OBSTRN04 AREA:sounding 与 QUAPNT01 丢失
|
||||||
|
|
||||||
|
OpenCPN AREA 有 VALSOU 时:
|
||||||
|
|
||||||
|
- 先 LS
|
||||||
|
- 再追加 sounding
|
||||||
|
- 最后追加 QUAPNT01
|
||||||
|
|
||||||
|
当前 Python AREA 有 VALSOU 时只返回 LS。
|
||||||
|
|
||||||
|
### 6.5 OBSTRN04 isolated AREA:QUAPNT01 丢失
|
||||||
|
|
||||||
|
OpenCPN isolated area:
|
||||||
|
|
||||||
|
`AC + AP + LS + ISODGR51 + QUAPNT01`
|
||||||
|
|
||||||
|
当前:
|
||||||
|
|
||||||
|
`AC + AP + LS + ISODGR51`
|
||||||
|
|
||||||
|
少 QUAPNT01。
|
||||||
|
|
||||||
|
### 6.6 WRECKS02 POINT:QUAPNT01 丢失
|
||||||
|
|
||||||
|
OpenCPN POINT:
|
||||||
|
|
||||||
|
- isolated:`ISODGR51 + QUAPNT01`
|
||||||
|
- 有 VALSOU:`DANGERxx + TX('Wk') + [WRECKS07] + SOUNDG03 + QUAPNT01`
|
||||||
|
- 无 VALSOU:WRECKS symbol + QUAPNT01
|
||||||
|
|
||||||
|
当前 Python resolver 的这些分支均未完整追加 QUAPNT01。
|
||||||
|
|
||||||
|
### 6.7 WRECKS02 AREA:QUAPNT01 丢失
|
||||||
|
|
||||||
|
OpenCPN AREA:
|
||||||
|
|
||||||
|
line/fill/isolated danger/sounding 后仍追加 QUAPNT01。
|
||||||
|
|
||||||
|
当前 Python area path 不完整。
|
||||||
|
|
||||||
|
### 6.8 Web runtime 的 SOUNDG 不是 OpenCPN SOUNDG03
|
||||||
|
|
||||||
|
本次为危险物补的:
|
||||||
|
|
||||||
|
`runtime_depth_text="10.5"`
|
||||||
|
|
||||||
|
可以恢复肉眼数字,但它不是 OpenCPN SOUNDG03 的真正实现。
|
||||||
|
|
||||||
|
OpenCPN SOUNDG03 会根据:
|
||||||
|
|
||||||
|
- SAFETY_DEPTH
|
||||||
|
- TECSOU
|
||||||
|
- QUASOU
|
||||||
|
- STATUS
|
||||||
|
- QUAPOS
|
||||||
|
- 深度整数/小数位
|
||||||
|
- 正负深度
|
||||||
|
- sounding symbol family
|
||||||
|
|
||||||
|
生成多枚:
|
||||||
|
|
||||||
|
`SOUNDSxx / SOUNDGxx`
|
||||||
|
|
||||||
|
symbol sequence。
|
||||||
|
|
||||||
|
当前 Web runtime 是普通 MapLibre text。
|
||||||
|
|
||||||
|
因此:
|
||||||
|
|
||||||
|
**“数字出现”不等于 1:1 S-52 sounding 已完成。**
|
||||||
|
|
||||||
|
### 6.9 WRECKS 的 TX('Wk') / WRECKS07 当前 Web runtime 未执行
|
||||||
|
|
||||||
|
Python resolver 已能生成部分完整 instruction。
|
||||||
|
|
||||||
|
但 runtime mode 下这些 instruction 不会被发送给 Web;Web 只根据 facts 自行选择主要 hazard symbol 和 depth text。
|
||||||
|
|
||||||
|
所以以下 OpenCPN primitives 当前可能不出现:
|
||||||
|
|
||||||
|
- `TX('Wk',...)`
|
||||||
|
- `SY(WRECKS07)`
|
||||||
|
- QUAPNT01 symbol
|
||||||
|
- SOUNDG03 glyph chain
|
||||||
|
|
||||||
|
### 6.10 LIGHTS05 实际应审 LIGHTS06
|
||||||
|
|
||||||
|
OpenCPN 参考源码:
|
||||||
|
|
||||||
|
`LIGHTS05() → return LIGHTS06(param)`
|
||||||
|
|
||||||
|
因此审计目标必须是 LIGHTS06 的真实流程,而不是函数名 LIGHTS05 后面的旧不可达代码。
|
||||||
|
|
||||||
|
本次已核对:
|
||||||
|
|
||||||
|
- 非 sector / sector CA 半径逻辑;
|
||||||
|
- sector angle +180° 转换;
|
||||||
|
- LITVIS obscured sector;
|
||||||
|
- all-round CA;
|
||||||
|
|
||||||
|
当前核心 CA 逻辑与 OpenCPN LIGHTS06 基本一致。
|
||||||
|
|
||||||
|
但 light description 的同位置去重/偏移规则仍应单独建立 branch test,不能仅靠已有 screenshot 判 PASS。
|
||||||
|
|
||||||
|
## 7. Rule Hint 压缩本身也存在风险
|
||||||
|
|
||||||
|
`presentation.py::static_rule_hints()` 明确是:
|
||||||
|
|
||||||
|
> Materialize compact scalar hints; full rule IR remains lookup-side.
|
||||||
|
|
||||||
|
它对于同类 primitive 通常只存第一个 scalar:
|
||||||
|
|
||||||
|
- first SY
|
||||||
|
- first AC
|
||||||
|
- first AP
|
||||||
|
- first LC
|
||||||
|
- first LS
|
||||||
|
|
||||||
|
虽然 `materialize_symbol_hints()` 在多 SY 时可以生成 `s52_symbol_rule_sequence`,但当前 style compiler 并不是一个通用的“有序 instruction executor”。
|
||||||
|
|
||||||
|
所以即使非 runtime CSP,也必须审计:
|
||||||
|
|
||||||
|
**原始 rule chain 是否在 scalar 化之后仍然保持所有可见 primitive。**
|
||||||
|
|
||||||
|
## 8. Style Compiler 与 OpenCPN renderer 的架构差异
|
||||||
|
|
||||||
|
OpenCPN 是通用 rule executor。
|
||||||
|
|
||||||
|
当前 style compiler 是“按已知 materialized fields 拼 MapLibre layers”。
|
||||||
|
|
||||||
|
当前 static coverage gate 能检查:
|
||||||
|
|
||||||
|
- AC
|
||||||
|
- AP
|
||||||
|
- LS
|
||||||
|
- LC
|
||||||
|
- SY
|
||||||
|
- LIGHT_SY
|
||||||
|
- TX/TE
|
||||||
|
- LIGHT_TX
|
||||||
|
|
||||||
|
但它检查的是“某 render kind 有 layer 消费”,不是:
|
||||||
|
|
||||||
|
`OpenCPN rule #1 → #2 → #3 ... 是否全部、按顺序、按条件等价执行`
|
||||||
|
|
||||||
|
Runtime coverage 更粗:
|
||||||
|
|
||||||
|
- RUNTIME_DEPARE
|
||||||
|
- RUNTIME_DEPCNT
|
||||||
|
- RUNTIME_SOUNDG
|
||||||
|
- RUNTIME_OBSTRN
|
||||||
|
- RUNTIME_WRECKS
|
||||||
|
- RUNTIME_CA
|
||||||
|
|
||||||
|
当前 capability 只能回答“某类 renderer 是否存在/preview”,不能回答:
|
||||||
|
|
||||||
|
**OBSTRN04 的 7 个 primitive 是否一个都没漏。**
|
||||||
|
|
||||||
|
## 9. 为什么现有审计没有提前抓到 10.5
|
||||||
|
|
||||||
|
现有审计能证明:
|
||||||
|
|
||||||
|
- Source data 存在;
|
||||||
|
- PBF facts 存在;
|
||||||
|
- CSP 名存在;
|
||||||
|
- runtime capability 有一个 renderer;
|
||||||
|
- 页面确实画了 hazard symbol。
|
||||||
|
|
||||||
|
但它没有建立:
|
||||||
|
|
||||||
|
`OpenCPN OBSTRN04 branch → expected ordered primitive sequence`
|
||||||
|
|
||||||
|
因此:
|
||||||
|
|
||||||
|
`SY(DANGER51)`
|
||||||
|
|
||||||
|
和:
|
||||||
|
|
||||||
|
`SY(DANGER51) + SOUNDG03(10.5) + QUAPNT01 + OBJNAM TX`
|
||||||
|
|
||||||
|
在旧 capability audit 中都可能被归为同一个:
|
||||||
|
|
||||||
|
`RUNTIME_OBSTRN`
|
||||||
|
|
||||||
|
粒度过粗。
|
||||||
|
|
||||||
|
## 10. 应建立的新审计:OpenCPN Source-First Rendering Equivalence
|
||||||
|
|
||||||
|
建议新增独立审计层,不依赖最终 PBF screenshot。
|
||||||
|
|
||||||
|
### Gate A — OPENCPN_CSP_BRANCH_COVERAGE
|
||||||
|
|
||||||
|
从 OpenCPN `s52cnsy.cpp` 固定 commit 提取每个目标 CSP 的 branch matrix。
|
||||||
|
|
||||||
|
至少记录:
|
||||||
|
|
||||||
|
- procedure
|
||||||
|
- geometry type
|
||||||
|
- object class
|
||||||
|
- attribute conditions
|
||||||
|
- mariner parameters
|
||||||
|
- called sub-procedures
|
||||||
|
- ordered output primitives
|
||||||
|
|
||||||
|
当前第一批:
|
||||||
|
|
||||||
|
- LIGHTS06
|
||||||
|
- SOUNDG02 / SOUNDG03
|
||||||
|
- UDWHAZ03
|
||||||
|
- OBSTRN04
|
||||||
|
- WRECKS02
|
||||||
|
- DEPARE01
|
||||||
|
- DEPCNT02
|
||||||
|
- QUAPOS01 / QUAPNT01 / QUALIN01
|
||||||
|
- SLCONS03
|
||||||
|
- RESTRN01
|
||||||
|
- RESARE02
|
||||||
|
|
||||||
|
### Gate B — CSP_RULE_SEQUENCE_EQUIVALENCE
|
||||||
|
|
||||||
|
对每个 branch,比较:
|
||||||
|
|
||||||
|
`OpenCPN expected ordered rule sequence`
|
||||||
|
|
||||||
|
与:
|
||||||
|
|
||||||
|
`CM95 resolver expected ordered rule sequence`
|
||||||
|
|
||||||
|
不是只比较主要 symbol。
|
||||||
|
|
||||||
|
例如:
|
||||||
|
|
||||||
|
Expected:
|
||||||
|
|
||||||
|
`SY(DANGER51), SOUNDG03, QUAPNT01, TX(OBJNAM)`
|
||||||
|
|
||||||
|
Actual:
|
||||||
|
|
||||||
|
`SY(DANGER51), SOUNDG03`
|
||||||
|
|
||||||
|
必须 FAIL。
|
||||||
|
|
||||||
|
### Gate C — RUNTIME_PRIMITIVE_COVERAGE
|
||||||
|
|
||||||
|
对于 mariner-dependent CSP,检查 expected primitive 是否存在对应 runtime executor:
|
||||||
|
|
||||||
|
- SY
|
||||||
|
- TX
|
||||||
|
- TE
|
||||||
|
- LS
|
||||||
|
- LC
|
||||||
|
- AC
|
||||||
|
- AP
|
||||||
|
- CA
|
||||||
|
- SOUNDG/MPS
|
||||||
|
- OP / DisplayBase side effects
|
||||||
|
- nested QUAPOS
|
||||||
|
|
||||||
|
不能再只检查 `RUNTIME_OBSTRN` 一个粗粒度 flag。
|
||||||
|
|
||||||
|
### Gate D — REAL_DATA_BRANCH_COVERAGE
|
||||||
|
|
||||||
|
扫描实际 Dataset,把真实出现过的 attribute combination 映射到 OpenCPN branch signature。
|
||||||
|
|
||||||
|
如果实际数据中存在某个 branch,而该 branch 没有 equivalence fixture:
|
||||||
|
|
||||||
|
FAIL。
|
||||||
|
|
||||||
|
这能避免只测试“理论上典型的几条”。
|
||||||
|
|
||||||
|
### Gate E — RENDER_CHAIN_CONSERVATION
|
||||||
|
|
||||||
|
建立:
|
||||||
|
|
||||||
|
`Source → normalized S57 view → Lookup → CSP branch → ordered Rule IR → PBF/runtime facts → renderer primitive execution`
|
||||||
|
|
||||||
|
逐段 signature。
|
||||||
|
|
||||||
|
任何阶段删掉 primitive 都失败。
|
||||||
|
|
||||||
|
## 11. 推荐架构修正
|
||||||
|
|
||||||
|
审计能发现问题,但要从根本上减少问题,建议取消“Python 一套条件 + JS 再写一套条件”的结构。
|
||||||
|
|
||||||
|
推荐:
|
||||||
|
|
||||||
|
`OpenCPN-compatible CSP Decision IR / Runtime Rule IR`
|
||||||
|
|
||||||
|
流程改为:
|
||||||
|
|
||||||
|
`Source facts → 唯一 CSP resolver → ordered Rule IR → 平台 renderer`
|
||||||
|
|
||||||
|
例如 OBSTRN04 resolver 输出:
|
||||||
|
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{"op":"SY","code":"DANGER51"},
|
||||||
|
{"op":"SOUNDG03","depth":10.5,"...":"..."},
|
||||||
|
{"op":"QUAPNT01","quapos":4},
|
||||||
|
{"op":"TX","field":"OBJNAM","...":"..."}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
Web / NavSea renderer 只负责执行 IR,不再自己判断 WATLEV/VALSOU/CATWRK。
|
||||||
|
|
||||||
|
这样 OpenCPN 决策树只翻译一次。
|
||||||
|
|
||||||
|
## 12. 当前审计结论
|
||||||
|
|
||||||
|
当前实现不能称为“OpenCPN 渲染流程 1:1 等价”。
|
||||||
|
|
||||||
|
可以确认:
|
||||||
|
|
||||||
|
- CM93 → S52 lookup 主链已经较完整;
|
||||||
|
- static resource contract 已较强;
|
||||||
|
- LIGHTS multi-presentation 守恒已有独立审计;
|
||||||
|
- CA screen-space renderer 已较完整;
|
||||||
|
|
||||||
|
但 mariner-dependent CSP 存在明确的 Rule Chain 丢失:
|
||||||
|
|
||||||
|
- OBSTRN04:FAIL
|
||||||
|
- WRECKS02:FAIL
|
||||||
|
- SOUNDG runtime:FAIL(当前为文字近似,不是 SOUNDG03 glyph executor)
|
||||||
|
- DEPARE/DEPCNT:仍为 PREVIEW_ONLY,需按 branch 审计
|
||||||
|
- LIGHTS06:核心 CA 已接近,但完整 branch/label sequence 尚未建立 source-first equivalence gate
|
||||||
|
|
||||||
|
因此下一阶段不建议继续按截图逐项补。
|
||||||
|
|
||||||
|
应先建立:
|
||||||
|
|
||||||
|
1. OpenCPN CSP branch manifest;
|
||||||
|
2. ordered Rule IR;
|
||||||
|
3. CSP rule-sequence equivalence audit;
|
||||||
|
4. runtime primitive coverage audit;
|
||||||
|
|
||||||
|
然后再修 renderer。
|
||||||
|
|
||||||
|
人工截图应降级为最终视觉验收,而不再承担发现业务分支遗漏的职责。
|
||||||
Reference in New Issue
Block a user