# MAG160C 官方管线完整逆向结果(Ghidra 全量,2026-08-13) > 本文档记录 CoreSDKLib.dll 官方渲染管线的完整反编译结论、逐像素验证结果, > 以及 demo3 旧版"鬼影"问题的根因分析。所有结论均有 Ghidra 反编译代码和 > 官方活体抓帧数据的双重证据,不是推测。 > > 工具链:Ghidra 12.1.2(headless 批处理反编译)+ IDA Free 8.4(交互确认)。 > 反编译产物:`analysis/ida/export/ghidra_dump/`(keyfuncs / callers / exports)。 --- ## 1. 官方管线(Ghidra 反编译确认) ``` USB 帧 (type=0, 0x1bb1b11b 标记) → 帧解析器 0x18001d930 验证: [0x08]=len, [0x1c+len]=0x1bb1b11c, type∈{0,1} 快门: width=160 时 = 帧尾字 frame[len+0x24] metadata 0x4c 字节: {0, counter, len, 1, 0, w, dev54, 8, 0, -50000, shutter-500, shutter, 0, 0, type, 0, 0} → 帧调度 0x180009ee0(FFC 状态机 + ref/表重建时序, 见 §3) → 主渲染 0x18000ca30 → 0x18000c950: f20 平滑器 0x41f10 (mode=1: 直通) → NUC → NUC 查表 0x180017200: d2 = (f20[i] - ref[i]) >> 1 seg: 每像素 (nsegs-1) 个 signed 阈值顺序比较 out = off + ((gain * d2) >> shift), shift=0x11c=12 clamp [0, 65535] → 盲元补偿 0x180017330: 固定记录表 dev+0x41878, 数量 dev+0x41654[sel] 每条 40 字节: {u32 target, u32 type(3..8=邻域数), u32 neigh[8]} out[target] = mean(neigh) (type 8/4 用移位, 其余用除法) → temporal filter 0x18001e920: 仅 dev+0x41fe0>1 时启用, 当前=1 跳过 → 统计 0x180010780: min/max/mean/std → dev+0x42010..0x42024 → 窗口 0x1800109c0 + 半宽 0x1800108a0: half = max(128, dev24*1000 >> dev4c_shift) / 2 (live: 5*1000>>3 /2 = 312) lo = min(fmin, mean-half) >= 0 hi = max(fmax, mean+half) <= 65535 → LUT1024 重建 0x180011950 + 0x180011ee0(直方图均衡, 见 §5) → 灰度: gray = LUT[(nuc-lo) * 0xffc00000/(hi-lo) >> 22] → 2x 升采样 0x180019740(2-tap 双线性 + 边缘外推, 见 §6) → 调色板 dev+0xb18 (256×4 BGR) ``` ## 2. DDT 校准文件(表的数据源) - 位置:`%TEMP%\Core<序列号>`(无扩展名),由 app 用 MAG_SaveDDT 保存、 MAG_StartProcessImage → 0x18000f130 在启动时加载;文件路径存 dev+0x4134c。 - 本机文件已复制:`build-artifacts/mag160c_official.ddt`(1,856,416 字节,v3)。 ``` v3 格式 (magic 0x5aa50003): +0 u32 magic +4 u32 width (160) +8 u32 height (120) +12 u32 端点数量 (0x41558, live=6) +16 u32 nsegs (0x41554, live=3) +20 u32 0x47944 (8, 2 的幂) +24 u32 0x47948 (-50000) +28 u32 0x4155c (1024) +32 u32 0x41560 (0) +36 i32 T[count] 端点温度: [8304, 18390, 28495, 33637, 38730, 47664] +.. u32 0x415b4[count] (偏移基准) +.. u32 0x41604[count] +.. u32 0x41654[count-1] 每端点盲元记录数 (live: 32×5) +128 端点表块 × count: thr (nsegs-1)*npix + 0x4155c/2 个 int16 (像素主序) gain nsegs*npix*2 个 uint16 (seg 主序: [seg][pix].gain/.off) +.. 盲元记录 × count (每端点 40 字节 × 数量) +.. trailer: 0x6bb60001, "lens f6.5" ``` ## 3. 端点选择 + 表插值 + ref 时序(0x180009ee0 / 0x180016ae0) ### 3.1 快门温度 = 设备温度代理 - 快门 = 帧尾字(width=160 时 frame[len+0x24]),每帧进入 metadata。 - dev+0x5c = 当前快门,dev+0x54 = 上次 FFC(0) 时的快门。 - FFC 条件:`idx >= N0+1800`(周期)或 `(|dev54 - 快门累加| > 250 && idx >= 冷却)`。 ### 3.2 端点选择(实测验证) ``` sel = 0 while (sel < count-2 && 快门 > T[sel+1]) sel++ ``` live 实测:快门 29289 → sel=2(T[2]=28495 < 29289 ≤ T[3]=33637)✓ ### 3.3 Q12 插值(0x180016dd0 / 0x180016f10) ``` t = ((快门 - T[sel]) << 12) / (T[sel+1] - T[sel]) clamp ±0x3fff work[i] = a[i] + ((b[i] - a[i]) * t >> 12) ``` 验证:阈值 0/38400 误差,gain 0/115200 误差。 ### 3.4 ref 采集时序(关键!) ``` FFC(0) 后帧计数复位: idx 1..5 : 表重建(分块) idx 6..9 : 采集 ref —— 4 个 type=1 校准帧的均值(累加 >> 2) (idx 6 复位平滑器,idx 9 发 FFC(1)) idx 10..13 : 表重建 idx 14+ : 正常渲染(ref 冻结,直到下次 FFC) ``` - **ref = 4 个 type=1 帧的均值**,不是场景帧! - type=1 帧均值实测 ~15000-16300,type=0 场景帧 ~11700-13700, 因此官方 d = f20 - ref ≈ **-2200 ~ -2700,恒 ≤ 0**。 - ref 在每次 FFC 对之后重新采集;FFC 之间完全冻结。 ## 4. 逐像素验证(官方抓帧 vs 反推重建) 用官方同帧抓帧(`analysis/pairs_verify_20260813/`)+ DDT 离线复算: | 环节 | 结果 | |---|---| | 阈值插值 (t 由快门算) | diff 0/38400 | | gain/off 插值 | diff 0/115200 | | NUC 查表 | diff 30/19200(MAE 0.118)| | NUC + 盲元补偿 | **diff 0/19200(MAE 0.0000)** | | 窗口 [lo,hi] | 与官方完全一致 | | 灰度 (LUT+idx) | 160 层 19200/19200 | | 2x 升采样 | **diff 0/76800** | | demo3 C 实现离线复算官方数据 | NUC/窗口/灰度全部 0 误差 | LUT 填充公式:在正确 center 下 834/1024 精确,其余 ±1~±58 为 跨帧捕获偏差(h1024/cdf/lut 不同帧)+ center 时域平滑所致。 ## 5. LUT1024 重建算法(0x180011950 + 0x180011ee0) 1. 1024-bin 直方图:bin = ((nuc-lo) * 0xffc00000/(hi-lo)) >> 22(u32 算术) 2. 3-tap 就地平滑(bin 0..1022) 3. 双向 CDF(从 mean-bin 向两侧累积): - 向下:每累积 u21(=total>>12) 个计数 +0x100 - 向上:<8*u21 时每 u21 个 +0x100,>=8*u21 时每 bin +0x200 4. 对比度曲线 curve[k] = max(1, (0x300000 + k*0x800)>>13) 5. 中心值二分搜索(fmin/fmax 对应 LUT 端约束) 6. center 时域平滑 + 重填 ## 6. 2x 升采样(0x180019740,逐像素验证) ``` 内部: out[2y][2x]=a out[2y][2x+1]=(a+b)>>1 out[2y][2x+2]=b out[2y][2x+3]=(c+b)>>1 out[2y+1][2x]=(d+a)>>1 out[2y+1][2x+1]=(d+e+a+b)>>2 out[2y+1][2x+2]=(e+b)>>1 out[2y+1][2x+3]=(f+e+c+b)>>2 右缘: out[2y][2W-1] = (3a>>2)+(l>>2)(外推) 底缘: out[2H-1][2x] = (3a>>2)+(u>>2) 角落: (3a+l)>>2 / (3a+u)>>2 / 三者均值 ``` ## 7. 鬼影根因分析(为什么旧 demo3 有鬼影) 旧 demo3(v4,SBNUC 版)与官方的差异,每一条都有上面的证据: ### 7.1 ref 语义完全不同(最根本) | | 官方 | 旧 demo3 | |---|---|---| | ref 来源 | FFC 窗口 4 个 **type=1 校准帧**均值 | 启动场景 12 帧中值 | | ref 值域 | 场景 + ~2200 counts(d 恒负)| ≈ 场景(d ≈ 0)| | 刷新时机 | **每次 FFC 对之后** | 仅启动一次(FFC 后只 rebase 全局偏置)| | FFC 之间 | 冻结 | 冻结 + SBNUC 自愈 | 后果: - 官方 NUC 输出 ≈ 9800(off 段配合 d 恒负),旧 demo3 输出 ≈ 10900+(d≈0), 两者对同一场景的画面等级都不同,更谈不上残影行为一致。 - 旧 demo3 启动采集一次 ref 后永不刷新 → 场景基线漂移(如设备升温、 环境温度变化)后 ref 失配 → 旧位置出现"黑洞/黑拖影",且只能等手动 FFC。 - 官方每次 FFC 重采 ref → 残影最多持续到下次 FFC(周期 120s 或快门漂移 250 触发), 且表也按新快门重建,匹配始终成立。 ### 7.2 NUC 表不是静态的 - 官方:表 = DDT 端点按快门温度 Q12 插值,FFC 后重建。 - 旧 demo3:静态抓取表(某次会话的插值结果),温度漂移后失配, 表现为"小温差残影擦除慢"——本质是表与当前温度不匹配造成的残差, 不是真正的显示层鬼影。 ### 7.3 SBNUC 是非官方 hack,引入次级鬼影 - 旧 demo3 的"曾热变冷快速吸收/低温静止不吸收"状态机,会把低温物体 排除在 ref 之外 → 物体离开后留下亮残影;把曾热像素写回 ref → 改变局部基线 → 新黑洞。 - 官方没有 SBNUC:ref 冻结 + 定期 FFC 刷新是官方唯一的"去鬼影"机制。 ### 7.4 盲元补偿缺失 - 官方有 32 条固定盲元记录(邻域平均),旧 demo3 完全没接 → 死像素 直接显示(0 值黑点),曾用动态 badmap 尝试 → 反而把场景结构钉在 屏幕坐标(BADMAP_ENABLE=0 后残留黑点)。 ### 7.5 窗口/灰度差异 - 官方窗口 = [min(fmin, mean-312), max(fmax, mean+312)]; 旧 demo3 用 P2..P98 自适应 → 对比度行为不同,但不是鬼影主因。 ### 7.6 结论 旧 demo3 的"鬼影"是 ref 语义错误 + ref/表不随 FFC 刷新 + 静态表 + 非官方 SBNUC 共同造成的,与官方"冻结 ref 的轻微旧位置偏暗"不是 同一个现象。修复 = 完整复刻官方管线(demo3 v5 已实现,见下)。 ## 8. demo3 v5(官方复刻)实现状态 - DDT 加载(v3 解析,含端点表/盲元记录) - 端点选择 + Q12 插值(快门驱动,FFC 后重建) - ref = FFC 窗口 4 个 type=1 帧均值(u32 累加器) - NUC + 盲元补偿(32 条固定记录) - 窗口/统计(官方公式) - LUT1024 重建(直方图均衡 + center 平滑) - 灰度 + 2x 升采样 + 官方调色板 - FFC:周期 1800 / 快门漂移 250 / 13 帧隐藏周期 - 实机运行:sel=2、FFC 自动触发正常、画面统计接近官方 (demo3 mean 9377 vs 官方 9640) ### 遗留问题 1. ~~demo3 NUC 输出有 2 个零像素~~ **已修复(2026-08-13 晚)**: 根因是 demo3 `load_ddt` 读取布局错误——blind 记录区在文件所有端点块之后, 代码却在每个端点块内读了 blind,导致 EP1+ 表整体错位 1280 字节。 错位表在死像素列附近 seg0 gain=35321(应为 ~4000),d2 踩中后 NUC clamp 到 0。 修复后全帧 0 个零像素,(65,25) nuc=9814,gain=4000 正常值。 2. LUT 重建的跨帧一致性:算法已按反编译实现并实机运行,同场景 gray 直方图 与官方相关性 0.9197(跨会话),剩余差异来自会话间 ref/快门温度差异, 属官方物理机制,不是 bug。 ## 9. 关键文件 - 反编译全量:`analysis/ida/export/ghidra_dump/` - `keyfuncs_decomp_CoreSDKLib.dll.txt`(核心函数伪代码) - `callers_decomp_CoreSDKLib.dll.txt`(调用关系 + 主管线) - `exports_decomp_CoreSDKLib.dll.txt`(全部导出函数) - `exports_decomp_ThermalSDK.dll.txt` - `all_functions_decomp_libcoresdk_arm64.so_00100000.txt`(ARM64 21.6MB) - DDT 文件:`build-artifacts/mag160c_official.ddt`、`analysis/ida/Core160043865.ddt` - 官方抓帧:`analysis/pairs_verify_20260813/`、`analysis/pairs_final_20260813/` - demo3 v5:`csdk/tools/mag160c_demo3.c` - 采集工具:`csdk/tools/tsdk_pair3.c`(含 DDT 复制、表/盲元/快门/窗口抓取)