Files
MAG160C/analysis/history/交接_完整逆向工程说明_20260811.md
T

35 KiB
Raw Blame History

MAG160C 热像仪逆向工程完整交接说明

1. 文档目的

本文档用于把 C:\Project\MAG160C 整个工程移交给另一位工程师或另一位 AI。目标不是只说明当前代码怎么编译,而是把以下内容全部交代清楚:

  • 工程每个主要目录的用途。
  • MAG160C 硬件、USB、帧格式和驱动环境。
  • 官方 Windows、Android、Linux SDK 的逆向成果。
  • 官方 CoreSDKLib.dll 实际渲染主链路。
  • mag160c_demo3.c 当前实现和所有实验性分支。
  • 已经解决的问题、仍未解决的问题和曾经失败的路线。
  • 如何安装工具、构建程序、连接设备、抓帧、运行和诊断。
  • 如何继续逆向低温残影,并避免重复走已经验证失败的路线。
  • 让下一位 AI 从零开始接手时可以直接使用的中文提示词。

文档日期:2026-08-11。工作目录:C:\Project\MAG160C。远端仓库:https://git.zxcli.top/zxc/MAG160C.git

2. 一分钟结论

2.1 硬件

  • 设备是 MAG160C 160×120 红外热像仪。
  • USB 设备常见身份是 VID 0x833C、PID 0x0001
  • 它不是普通 UVC 热像仪,而是厂商自定义 USB bulk 协议。
  • 命令使用 EP 0x03,响应使用 EP 0x82,实时帧使用 EP 0x81
  • 原始实时帧包含 160×120 个小端 uint16 像素。

2.2 官方渲染链路

官方 Windows CoreSDKLib.dll 的主链路已经被活体抓帧和反汇编共同确认:

USB type=0 原始帧
  -> f20 平滑器 0x41f10,当前 mode=1,基本是逐帧 memcpy
  -> NUC 0x180017200
       d2 = (f20[i] - ref[i]) >> 1
       每像素两个 signed threshold 选择三个 segment
       每像素 gain/off 查表
       out = off + ((gain * d2) >> 12)
       clamp 到 [0, 65535]
  -> 0x180017330,固定表驱动的坏点/盲元邻域补偿
  -> 可选 0x18001e920 temporal filter;当前实测 mode=1,通常跳过
  -> 窗口 hi/lo
  -> 1024 项灰度 LUT
  -> 256 项 BGR 调色板
  -> 320×240 输出

2.3 官方参考帧

  • 官方参考缓冲来自 dev+0x41ef0,对象起点是 dev+0x41ee0
  • 参考平滑器是 mode=4,启动阶段通过累计平均形成参考。
  • 正常 type=0 帧不会逐帧重新采集整幅参考。
  • FFC 后空间结构通常保持,只做全局基线变化或重新进入内部配置过程。
  • 官方参考不是 demo3 当前的“每帧根据场景自适应参考”。

2.4 当前 demo3 状态

当前 demo3 已经接入一组从官方活体抓取并验证过的三段逐像素 NUC 表:

  • build-artifacts/mag160c_official_nuc_gain.bin
  • build-artifacts/mag160c_official_nuc_thr.bin

当前默认策略是:

  • CONSTANT_REF=1
  • BADMAP_ENABLE=0
  • 官方表存在时使用三段逐像素 gain/off/threshold 查表。
  • 使用当前场景中值在官方 NUC 输出域反求一个全局参考偏置,使中值约为 NUC_CENTER=9804
  • SBNUC_ENABLE=1,但实际只允许“曾经进入热态、后来变成持续负残差”的像素在运动结束后自愈。
  • 正残差不会写入参考,避免热目标被吸收。
  • 显示层 deghost_nuc() 不调用。

当前仍未完全解决:

  • 低温区域静止时间较长后仍可能出现残影。
  • 某些固定参考失配会表现为黑洞。
  • 官方每次 FFC/传感器温度区间变化后会重新生成 NUC 表;demo3 当前使用一组静态抓取表,没有完整复刻官方温度端点插值和运行时重建。
  • 官方 0x180017330 的固定盲元补偿记录还没有完整接入 demo3 的新 NUC 表路径。
  • 因此当前版本是比线性近似更接近官方的研究基线,不应宣称已经完全复刻官方。

3. 目录说明

3.1 根目录

路径 用途
README.md 仓库入口和最短说明。
交接_完整逆向工程说明_20260811.md 本完整交接文档。
findings.md 早期和中期的综合逆向发现,包含 Windows、Android、Linux SDK 证据。
progress.md 按日期记录的长期工程进度、协议恢复和工具安装记录。
task_plan.md 早期 Linux SDK 实现计划和历史任务状态。后续热像渲染工作应以本交接文档和 analysis/handoff_*.md 为准。
frame_stats.txt 历史原始帧统计。
pair_*.winpair_*.win2pair_*.nuctab 根目录中遗留的官方抓帧窗口、字段和短 NUC 表样本。完整抓帧在 analysis/pairs_*
.gitattributes Git LFS 大文件规则。
.tools/ 逆向分析工具、Python 包和下载缓存。
.agents/ 当前为空,保留目录结构。
.superpowers/ 早期计划工具的资料,当前不参与 demo3 运行。
app/ 原厂 APK 和 PC 分析程序。
IR_Camera_SDK-1.0.1/ 原厂 Windows、Android、Linux SDK、示例、驱动、DLL、SO 和文档。
vendor-docs/ 从根目录整理出的模块硬件文档。

3.2 csdk/

这是当前维护的纯 C SDK 和 Windows 工具目录。

路径 用途
csdk/include/ 对外头文件和 C API。
csdk/src/mag160c_frame.c 0x1bb1b11b / 0x1bb1b11c 帧解析和流组装。
csdk/src/mag160c_ir.c libusb 设备打开、命令、启动、停止、FFC、帧线程。
csdk/src/mag160c_temp.c 原始响应、PWL 校正、T2E 温度换算。
csdk/src/mag160c_tcm.c TCM 的 0x7e 帧和校验。
csdk/src/mag160c_display.c 坏点、显示、FFC 调度、线性温度工具。
csdk/src/mag160c_tables.h 从官方二进制提取的温度表。
csdk/tools/mag160c_demo3.c 当前主要 Windows 直连、渲染和残影实验程序。
csdk/tools/mag160c_demo2.c 历史显示管线和 MOG/NUC 实验版本。
csdk/tools/tsdk_pair2.c 官方 ThermalSDK 活体同帧抓帧工具。已修正 gain/thr 文件覆盖问题。
csdk/tests/ C SDK 纯计算、协议、温度和显示测试。
csdk/third_party/libusb/win64/ Windows x64 libusb 头文件、DLL 和 MinGW 导入库。
csdk/CMakeLists.txt CMake 构建文件。当前环境没有 CMake,主要使用直接 GCC 命令。
csdk/WINDOWS_TESTING.md USBPcap、Wireshark、MinGW 和硬件测试指南。

3.3 analysis/

这是逆向工程的核心证据目录。

路径 用途
analysis/disasm/ Windows CoreSDK、Linux libmagcore、Android ARM64 的反汇编、符号和函数切片。
analysis/protocol_spec.md USB 协议、帧格式、命令、温度和 NUC 公式的正式规格。
analysis/progress_reverse.md 官方渲染主链和反汇编阶段性结论。
analysis/handoff_20260810.md demo2 早期路线、MOG、平场和问题演化。
analysis/handoff_20260811.md 较短的 2026-08-11 交接。
analysis/handoff_20260811_full.md 当前残影问题最重要的交接文档之一。
analysis/handoff_20260812.md 官方活体抓帧、NUC 和显示数据分析。
analysis/handoff_20260812_night.md 平滑器、参考和 NUC 查表的夜间逆向结论。
analysis/handoff_20260813.md 黑洞、SBNUC、Hardie/Kalman 路线的历史总结。
analysis/pairs_nuc2/ 官方 NUC 同帧数据。
analysis/pairs_move/ 官方移动目标数据。注意旧 .gain/.thr 曾被覆盖。
analysis/pairs_recheck_20260811/ 使用修复后的 tsdk_pair2_fixed.exe 重新捕获的数据,包含未覆盖的真实 .gain/.thr
analysis/pairs_long/ 较长时间官方抓帧和内部缓冲。
analysis/pairs_ffc/pairs_ffc2/ FFC 前后对比。
analysis/pairs_win/pairs2/pairs_bar/ 窗口、调色板和官方输出实验。
analysis/captures/ USBPcap、libusb shim 和官方/csdk 抓包。
analysis/usbnoise/ 原始噪声帧和元数据。
analysis/ourdump/ 直连设备原始抓帧。
analysis/reverse-cache/ AAR、JAR 等解包后的逆向缓存。
analysis/reverse_tools/ ARM64/ELF 辅助工具。
analysis/revtools.py ELF 加载、符号、交叉引用辅助脚本。
analysis/t2e_table.json T2E 单调表。
analysis/official_t2e_table.txt 官方 T2E 提取文本。
analysis/legacy-cpp/ 被纯 C SDK 替代的旧 C++ 实现和构建物。
analysis/demo2_*.png 历史实机验证截图。

3.4 build-artifacts/

该目录保存可直接运行的 Windows 程序、DLL、抓图、日志和实验产物。当前最重要的文件:

  • mag160c_demo3.exe:当前 demo3。
  • mag160c_official_nuc_gain.bin:官方三段 gain/off 表,230400 字节。
  • mag160c_official_nuc_thr.bin:官方 threshold 抓取文件,115200 字节;demo3 实际使用前 76800 字节中的两个 threshold/像素,尾部多余数据保留作证据。
  • tsdk_pair2_fixed.exe:修复 .gain/.thr 覆盖问题的官方抓帧工具。
  • demo3_diag.txtdemo3 每帧诊断日志。
  • demo3_ffc.txtFFC 调度日志。
  • demo3_rebase.txtFFC 后参考 rebase 日志。
  • demo3_auto_*.bmpdemo3 每 30 帧自动抓图。
  • official_*.binofficial_*.rgbofficial_meta.txt:官方活体输出样本。
  • csdk_*mag160c_*usb_*tsdk_*:历史测试和探针程序。

不要把 build-artifacts 当成唯一源代码目录。多数文件是从不同历史版本生成的,必须结合时间、文件名和日志判断是否属于当前版本。

4. 硬件和 USB 环境

4.1 设备身份

VID = 0x833C
PID = 0x0001(常见值,具体以设备描述符为准)
分辨率 = 160×120
帧像素数 = 19200
常见帧率 = 15 fps

4.2 Windows 直连端点

端点 方向 用途
0x03 主机到设备 命令写入。
0x82 设备到主机 命令响应。
0x81 设备到主机 实时帧。
0x84 设备到主机 大块数据或 DDT 相关读取,当前 demo3 不依赖。

4.3 原始帧布局

EP 0x81 数据以固定标记开始:

+0x00  u32  0x1BB1B11B
+0x04  u32  帧计数
+0x08  u32  数据长度,160×120 时为 0x9600
+0x0C  u32  帧类型,0 或 1
+0x10  u32  shutter/周期字段
+0x1C  38400 字节像素,19200 个小端 uint16
+0x1C+len u32 0x1BB1B11C

demo3 的第二次 bulk read 直接从 data[0] 读取 38400 字节像素;第一段 28 字节头已在 hdr 中读取。这是曾经修复过的关键偏移问题。

4.4 命令序列

当前设备实测的初始化序列:

0x6BB6B66B4 字节
0x6BB6B66C4 字节
0x6BB6B66F4 字节
0x6BB6B672,参数 0,8 字节,通常连续发送两次
等待约 300 ms
0x6BB6B6734 字节,启动

停止:

clear_halt(0x03)
clear_halt(0x82)
0x6BB6B6744 字节

FFC

0x6BB6B672 + uint32 参数

不要把普通 4 字节命令误写成 8 字节;当前设备对普通命令的 8 字节形式可能 STALL。只有带参数的 FFC 使用 8 字节。

5. 官方 SDK 和反汇编成果

5.1 SDK 文件

原厂 SDK 主要在:

IR_Camera_SDK-1.0.1/windows/windows/app/
IR_Camera_SDK-1.0.1/windows/windows/lib/x64/
IR_Camera_SDK-1.0.1/windows/windows/drivers/
IR_Camera_SDK-1.0.1/android/android/EloThermal/
IR_Camera_SDK-1.0.1/linux/1.2.2/1.2.2/EloThermal/

Windows 关键文件:

  • CoreSDKLib.dll:底层 MAG API、USB 和渲染主链。
  • ThermalSDK.dll:更高层的热像 API 包装。
  • libusb0.dll:官方旧版 libusb-win32 路径使用。
  • CameraSDK.dll:RGB/UVC 相机接口,不是红外原始帧主通道。

Android/Linux 版本提供了额外符号、JNI、C++ 类名和温度函数线索,对 ARM64 libcoresdk.so 的温度和校正逆向非常有帮助。

5.2 平滑器

通用平滑器函数大约位于 0x1800011a0

  • mode=1:当前 f20 实例使用,逐帧复制输入,不是多帧 EMA。
  • mode=2:两帧平均。
  • mode=4:累计平均,当前 ref 启动阶段使用。

对象:

dev+0x41ee0ref 平滑器对象
dev+0x41ef0ref 实际 uint16 缓冲指针
dev+0x41f10f20 平滑器对象
dev+0x41f20f20 实际 uint16 缓冲指针
dev+0x41fd8ref 相关 mode,实测为 4
dev+0x41fdcf20 相关 mode,实测为 1
dev+0x41fe0:后续可选处理模式,实测为 1

5.3 官方参考时序

启动帧计数和参数实测:

0x2d78 = 4
0x2d88 = 5
0x2d8c = 4
0x2d90 = 1800
0x2d94 = 250

启动早期的 c620 -> 0x41ee0 推帧会形成 ref。正常处理帧不会每帧重新采集整幅 ref。FFC 会切换 type=1 校准帧,官方应用在这段时间冻结显示。

5.4 官方 NUC 查表

0x180017200 的关键反汇编已经确认:

d2 = ((int)f20[i] - (int)ref[i]) >> 1;
seg = 0;
while (seg < nsegs - 1 && d2 > threshold[i][seg])
    seg++;
gain = gainoff[seg][i].gain;
off  = gainoff[seg][i].off;
out  = off + ((gain * d2) >> 12);
out  = clamp(out, 0, 65535);

实测:

nsegs = 3
每像素 threshold 数 = 2
每段每像素是一个 uint16 gain + 一个 uint16 off
gain/off 表总大小 = 3 × 19200 × 4 = 230400 字节
threshold 实际需要 = 2 × 19200 × 2 = 76800 字节
shift = dev+0x11c = 12

本工程通过修复后的 tsdk_pair2.c 抓取 pair_000 表,并用 PowerShell 离线复算:

官方 raw 与表公式重建的平均绝对误差约 0.118 counts
绝大多数像素完全一致
少量最大误差约 900 counts,来自后续盲元补偿尚未复刻

这是当前最可靠的官方 NUC 复刻证据。

5.5 NUC 表生成

相关函数:

0x180016ae0:按 dev+0x54 传感器温度选择端点区间
0x180016dd0threshold 的 signed Q12 分段插值
0x180016f10gain/off 的 unsigned Q12 分段插值
0x18000c760:把表重建分成多个 chunk 逐步写入

端点来自 DDT/配置,不来自当前 live、ref 或场景。官方表在启动重建和 FFC 后重建阶段变化;同一个重建组内的连续普通帧通常相同。当前 demo3 使用一组静态官方表,因此 FFC 后和传感器温度区间变化时还不完全等价。

5.6 NUC 后处理

0x180017330 不是全帧 3×3 模糊,而是固定记录表驱动的盲元/坏点补偿:

  • 只处理 dev+0x41878 指向的固定记录。
  • 原地修改 dev+0x270
  • 访问邻域像素并平均或按固定类型替换。
  • 没有场景历史,也不负责整幅图去鬼影。

0x18001e920 可能是输出 temporal filter,但当前 dev+0x41fe0=1 时实测路径跳过,不能把它当成官方当前画面的主要鬼影来源。

5.7 窗口、灰度和调色板

  • 官方窗口字段约为 dev+0x4202c / dev+0x42030
  • 静态背景最小跨度约 624 counts,即半宽约 312。
  • 有热目标时窗口会动态扩大。
  • dev+0x4284c 有 1024 项灰度 LUT。
  • dev+0xb18 有 256×4 BGR 调色板。
  • 同帧重建实验已经证明窗口和 LUT 是当前帧确定性映射,不保存上一帧历史,显示层不是大片鬼影的主要来源。

5.8 官方温度

demo3 采用已从官方提取的 T2E 表:

temp_mC = T2E[3 * nuc - C]
C 约为 5797,实际会话相关

主要代码在 counts_to_temp_mc()。T2E 数据在:

  • csdk/include/mag160c_official_t2e.h
  • analysis/t2e_table.json
  • analysis/official_t2e_table.txt

6. 当前 demo3 代码思路

6.1 输入和坏点

decode_live()

  1. 从第二次 bulk read 的 data[0] 解析 19200 个小端 uint16
  2. 如果启用固定坏点表则执行拓扑填充;当前 BADMAP_ENABLE=0,避免动态场景坏图钉在屏幕坐标。
  3. 执行 correct_zero_pixels(g_live),修复原始零值。

6.2 参考采集

  • 启动 FFC 后等待基线稳定。
  • REF_INIT_N=12 帧采集。
  • 每像素收集样本并做排序和中间 50% 平均。
  • 结果放入 g_ref_mura
  • g_ref_mura 同时承担固定 mura 图案和启动场景基线,这是当前低温残影的根本风险之一。

6.3 官方表加载

load_official_nuc_tables() 从程序工作目录加载:

mag160c_official_nuc_gain.bin
mag160c_official_nuc_thr.bin

official_nuc_value() 按官方公式选择每像素 segment 并计算输出。

6.4 全局偏置

calibrate_official_ref_bias()

  • 每帧抽取每 4 个像素中的一个。
  • 对候选 scalar bias 计算官方 NUC 输出直方图中值。
  • 二分搜索,使输出中值接近 NUC_CENTER=9804
  • 使用 g_ref_mura + g_official_ref_bias 作为查表参考。

这个步骤只跟踪全局漂移,不应把局部场景写进逐像素参考。

6.5 当前 SBNUC 实验分支

当前 SBNUC_ENABLE=1 的实际更新约束:

  • 正残差不更新参考。
  • 运动期间创新门控保持约 6 帧,不更新参考。
  • 负残差需要运动结束后连续 2 帧低于 SB_RECOVER_T=16
  • 还要求该像素之前处于 SB_HOT_RELEASE,也就是曾经为热态、随后离开。
  • 低温物体从头到尾的负残差不应进入自愈,避免移开后亮鬼影。

这仍是实验代码。旧的 g_hot_histg_sb_prev_dg_sb_innov_var 等数组和注释中保留了多轮失败方案,后继者应在确认新方案后清理,而不是继续叠加状态。

6.6 设置参考按钮

当前按钮文字是 Recenter bias,对应 WM_COMMAND1004

  • 如果官方表可用,只重新计算全局 bias。
  • 不再执行 g_ref_mura[i] = g_live[i]
  • 这是刚修复的严重问题;旧行为会把当前物体温度写进参考,导致设置参考后所有物体趋于相同,挪开后才出现高温/低温差异。
  • 没有官方表时才保留旧的 fallback 手动参考行为。

7. 已解决的问题

以下问题在历史实机验证中已经明显改善,不要在没有证据时推翻:

问题 原因 当前处理
颗粒感很重 demo2 早期 NUC 斜率过大,曾使用接近 3 的斜率 当前线性 fallback 为 NUC_GAIN=19,官方表路径已接入。
FFC 后颜色跳变 FFC 后重新采集场景参考,把场景写进 ref REF_REINIT_AFTER_FFC=0,type=1 不渲染,使用全局 rebase。
物体破碎、椒盐 固定窗口过窄 使用 P2..P98 自适应窗口和最小 624 跨度。
mura 蒙层 常数参考或错误平场直接显示传感器固定纹理 启动采集 g_ref_mura,并使用逐像素官方 NUC 表。
中上固定遮蔽 动态 badmap 将场景结构钉在屏幕坐标 BADMAP_ENABLE=0
右下炫光 把盲元补偿误当全帧 3×3 blur 删除错误整帧 blur 路径。
最高温点黑色 极值映射和零值处理不稳 idx >= hi 饱和到 1023,保留零值修复。
黑洞完全不消失 参考冻结且没有任何失配处理 当前保留受限的 post-motion self-heal,但仍有速度/误判权衡。
设置参考后物体被抵消 按钮把当前 live 整帧覆盖到 ref 改为 Recenter bias,不覆盖固定平场。

8. 已经尝试且效果不好的路线

8.1 单纯调 SBNUC 全局参数

尝试过:

  • 去掉帧差条件。
  • 放宽 dd 阈值。
  • alpha1/64 调到 1/16
  • 统一使用更快负残差步长。

结果:黑洞在某些情况下改善,但小温差或低温物体会被参考吸收,移开后残影回来;过激时画面稳定性变差。

8.2 显示层去鬼影

尝试过:

  • 最近热区 mask。
  • motion history。
  • recent hot mask。
  • 显示层邻居替换。
  • deghost_nuc()

结果:容易把真实目标抹掉,出现“有地方看不见”的副作用。当前调用被注释,后继者不要重新走这条路线。

8.3 复杂 MOG/自愈状态机

尝试过:

  • 逐像素 warm/release 状态。
  • 时域创新方差。
  • 运动保持计数。
  • persistent negative self-heal。

结果:可以在黑洞和鬼影之间移动问题。根本原因是一个 g_ref_mura 同时承载固定平场、场景基线和自适应背景,信息角色混在一起。

8.4 完全冻结参考

效果:热目标鬼影明显减少或消失。

副作用:当场景基线或局部低温发生变化时,旧位置会形成黑洞;黑洞自愈又可能吸收低温物体。这个折中证明需要拆分“固定传感器校正”和“场景背景模型”,而不是继续改一个 ref。

8.5 静态官方表

效果:比单一 0.19 + comp 更接近官方,热目标鬼影有明显改善。

局限:

  • 表来自某次官方会话,FFC/传感器温度变化后官方会重新插值。
  • 0x180017330 固定盲元补偿没有完整接入。
  • 表外的参考时序、内部 sensor temperature 和端点配置没有完全复刻。
  • 当前静态表不是最终官方复刻。

9. 当前未解决问题和最重要的下一步

9.1 低温长停留残影

现象:

  • 低温区域在画面中停留较长时间。
  • 参考或负残差处理可能逐渐改变局部基线。
  • 物体移动后出现残影,或者黑洞与亮鬼影互换。

最可能的根因:

  1. demo3 的 g_ref_mura 仍然是“固定平场 + 启动场景”混合物。
  2. post-motion self-heal 对“曾热后变冷”和“从头到尾低温”虽然已区分,但低对比度转换可能漏过状态阈值。
  3. 官方表当前是静态表,FFC/温度区间变化没有实时重建。
  4. 官方盲元补偿没有完全复刻,少量异常像素可能形成黑点或局部结构。
  5. 官方的实际 ref 绝对偏置和 demo3 的 g_ref_mura + scalar bias 不是同一个参考定义。

9.2 优先任务 A:复刻官方动态 NUC 表

需要继续做:

  1. tsdk_pair2_fixed 保存的 b01/b02/b04/b05 端点源表中确认 DDT 端点记录结构。
  2. 找到 USB 原始帧或设备状态中对应 dev+0x54 的传感器温度。
  3. 0x180016ae00x180016dd00x180016f10 的 Q12 整数公式,在 demo3 中按温度插值生成 threshold/gain/off。
  4. FFC 后重新生成表,但不重新把场景采入空间参考。
  5. 用多温度、多次 FFC 的官方 b03/b06raw 做逐像素回归。

9.3 优先任务 B:复刻官方盲元补偿

需要从官方活体捕获:

  • dev+0x41878 固定记录表。
  • dev+0x41654 对应的记录数量。
  • 每条记录的目标像素、类型和邻域索引。

然后把 0x180017330 的原地邻域补偿接到官方 NUC 表输出后。不要使用由场景动态生成的 badmap。

9.4 优先任务 C:拆分参考角色

推荐最终数据结构:

g_fixed_pattern[i]       只保存传感器固定空间校正
g_scene_reference[i]     可选、严格受状态控制的场景背景
g_global_ref_bias        全局传感器/FFC 漂移
g_official_nuc_table     官方温度配置表

当前 g_ref_mura 不应继续同时承担上述所有角色。

9.5 优先任务 D:受控实验数据

必须抓以下序列,每个序列至少 60 帧,最好包括 live/ref/raw/gain/thr/gray

  1. 空背景稳定 10 秒。
  2. 热目标进入、停留、离开、空背景恢复。
  3. 冷目标进入、停留、离开、空背景恢复。
  4. 小温差热目标重复移动。
  5. FFC 前后同一目标序列。

对每个像素同时画:

live
ref
live-ref
d2
official raw
demo3 raw
gray

不要只看 BMP。BMP 只能说明最终表现,不能定位残影是在 USB、ref、NUC、盲元补偿还是窗口阶段产生。

10. 环境安装和部署

10.1 当前开发环境

当前主要环境是 Windows PowerShell

工作目录:C:\Project\MAG160C
MinGW GCCC:\mingw64\bin\gcc.exe
GCC 版本:曾使用 14.2.0
Python:用于 PDF、二进制和逆向辅助
JavaZulu 21,用 javap 查看 Android 类
Git:用于提交和远端同步
Git LFS:用于大型二进制

10.2 必装工具

建议安装或确认以下工具:

  1. MinGW-w64,要求 gccg++arobjdumpreadelfstringsdlltoolgendef 可用。
  2. Git。
  3. Git LFS。
  4. Python 3.10 或更新版本。
  5. Java 21 或可用的 javap,仅在需要分析 Android JAR/AAR 时使用。
  6. PowerShell 5.1 或 PowerShell 7。
  7. 可选:CMake。当前环境中 cmake 不在 PATH,因此实际验证主要使用 GCC 直接命令。
  8. 可选:Wireshark 4.6.7 和 USBPcap 1.5.4,用于 USB 抓包。
  9. 可选:Zadig,用于切换 WinUSB/libusb-win32 驱动。

10.3 Python 逆向包

历史上使用过:

  • capstone
  • pyelftools
  • pdfplumber

工程中已有 .tools/python-revlibs/,不要假设系统 Python 一定有这些包。需要时可以先检查:

python --version
python -c "import capstone, elftools; print('逆向 Python 包可用')"

10.4 SDK 和 DLL

原厂文件已经随工程提供,通常不需要再次下载:

IR_Camera_SDK-1.0.1\windows\windows\app\CoreSDKLib.dll
IR_Camera_SDK-1.0.1\windows\windows\app\ThermalSDK.dll
IR_Camera_SDK-1.0.1\windows\windows\app\libusb0.dll
IR_Camera_SDK-1.0.1\windows\windows\lib\x64\CoreSDKLib.dll
IR_Camera_SDK-1.0.1\windows\windows\lib\x64\ThermalSDK.dll
csdk\third_party\libusb\win64\libusb-1.0.dll
csdk\third_party\libusb\win64\libusb-1.0.x64.a

官方 ThermalSDK.dll 路径和 demo3 的 libusb-1.0 路径不要混用。官方程序通常依赖 libusb0.dll/libusb-win32demo3 使用 libusb-1.0.dll 和 MinGW 导入库。

10.5 驱动注意事项

曾经验证过的 Windows 组合:

  • 原厂旧驱动签名在新 Windows 上可能失败。
  • Zadig 安装 WinUSB 或 libusb-win32 后,设备才能被对应程序打开。
  • 官方 DLL、TI 的 64 位 libusb-1.0.dll、WinUSB/libusb-win32 的组合行为不同。
  • 不要在官方程序、tsdk_pair2 和 demo3 同时占用设备。
  • 如果出现 no channel、STALL、设备不枚举,先停止所有程序并重新插拔 USB。

11. 构建和启动

11.1 demo3 当前编译命令

在 PowerShell 中执行:

注意:命令只允许出现一次 mag160c_error.c。重复传入会出现 multiple definition 链接错误。

11.2 启动 demo3

Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"

官方 NUC 表文件必须和 exe 在同一个工作目录中:

build-artifacts\mag160c_official_nuc_gain.bin
build-artifacts\mag160c_official_nuc_thr.bin

如果缺失,程序会退回旧的线性 NUC 路径,画面和残影表现不能与当前版本比较。

11.3 停止 demo3

Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue

必须先停止 demo3 才能运行官方 ThermalSDK 抓帧工具。

11.4 编译修复后的官方抓帧工具

tsdk_pair2.c 原来先保存真实指针表,后面又覆盖同名 .gain/.thr。当前修复版把后面的内联字段改存为 .gain_inline/.thr_inline

11.5 官方抓帧

先确认输出目录父目录存在,再执行:

New-Item -ItemType Directory -Path "C:\Project\MAG160C\analysis\pairs_recheck_20260811" -Force
Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
& "C:\Project\MAG160C\build-artifacts\tsdk_pair2_fixed.exe" "C:\Project\MAG160C\analysis\pairs_recheck_20260811" 12 6000

参数含义:

参数 1:输出目录
参数 2:抓取 pair 数
参数 3:官方 SDK 初始化等待毫秒数

tsdk_pair2.c 当前会在约三分之一和三分之二位置触发 FFC,适合验证 FFC 表变化,不适合完全无干预的纯运动实验。要做低温残影实验,建议另写不自动触发 FFC 的 harness。

11.6 日志和图像

demo3 工作目录下:

日志文件由程序重新启动时以写模式打开,重启会覆盖旧日志。要保留一轮实验,先复制到 analysis/ 并改名。

12. C SDK 构建和验证

如果已经安装 CMake

cmake -B build -S csdk
cmake --build build
ctest --test-dir build

当前机器没有 CMake 时,按照 csdk/WINDOWS_TESTING.md 使用 MinGW 直接编译。无硬件时可以验证:

build-artifacts\csdk_cli_libusb.exe frame-test
build-artifacts\csdk_cli_libusb.exe temp-test
build-artifacts\csdk_cli_libusb.exe tcm-rotate 5
build-artifacts\csdk_cli_libusb.exe tcm-light green blink
build-artifacts\csdk_cli_libusb.exe ir-info

预期 TCM 编码结果示例:

7e 00 07 85 02 77 00 01 00 05 7f

13. Git 和仓库维护

本工程原先只有空的 .git 目录,不是有效 Git 仓库。本次移交应初始化真实 Git 仓库并推送到:

https://git.zxcli.top/zxc/MAG160C.git

仓库约 0.824 GB,包含大量 DLL、EXE、APK、PDF、PNG、BMP、抓帧和反汇编二进制。已准备 Git LFS 规则。提交前必须检查:

不要把账号密码、访问令牌、私钥写入文档或代码。原厂 SDK 和 APK 可能受第三方许可限制,仓库接手者应确认仓库访问权限和内部使用范围。

14. 当前接手者的建议工作顺序

第一步:确认环境和版本

  1. 阅读本文、analysis/handoff_20260813.mdanalysis/handoff_20260811_full.md
  2. 检查 git status 和远端分支,确认没有并行改动。
  3. 检查设备 PID、驱动、DLL、表文件。
  4. 编译并启动当前 demo3,保存一轮不移动时的日志。

第二步:验证当前路径是否真的加载官方表

  1. 暂时把 mag160c_official_nuc_gain.bin 改名,启动一次并记录 fallback 画面。
  2. 恢复表文件,再启动并记录官方表画面。
  3. 比较 demo3_diag.txtg_sb_shift、NUC 统计和 BMP。
  4. 不要用缺失表文件的 fallback 版本评价当前算法。

第三步:做官方和 demo3 同帧采集

  1. 停 demo3。
  2. 用修复后的官方工具抓取至少 60 帧,不要自动触发 FFC。
  3. 保存 f20、ref、raw、gain、thr、gray、window、blind compensation 记录。
  4. 用脚本逐像素对比官方 raw 和 demo3 raw。

第四步:优先修官方动态表和盲元补偿

不要先改调色板、窗口、显示层。官方证据已经说明这两个阶段没有上一帧历史,低温残影优先查 ref、gain/off、threshold、FFC 表重建和盲元补偿。

15. 给下一位 AI 的接手提示词

下面的提示词可以直接复制给下一位 AI。它要求 AI 先理解工程,再动手,不要从头重做,也不要反复尝试已经失败的显示层补丁。

你现在接手 C:\Project\MAG160C 的 MAG160C 热像仪逆向工程。

设备:MAG160C160x120USB VID 0x833C,常见 PID 0x0001。
主程序:C:\Project\MAG160C\csdk\tools\mag160c_demo3.c
输出程序:C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe
官方 DLLIR_Camera_SDK-1.0.1\windows\windows\app\CoreSDKLib.dll 和 ThermalSDK.dll
远端仓库:https://git.zxcli.top/zxc/MAG160C.git

严格执行以下步骤:

1. 先读:
   - 交接_完整逆向工程说明_20260811.md
   - analysis/handoff_20260813.md
   - analysis/handoff_20260811_full.md
   - analysis/protocol_spec.md
   - csdk/tools/mag160c_demo3.c
   - csdk/tools/tsdk_pair2.c

2. 再检查:
   - git status --short --branch
   - build-artifacts/mag160c_official_nuc_gain.bin
   - build-artifacts/mag160c_official_nuc_thr.bin
   - analysis/pairs_recheck_20260811/
   - analysis/disasm/

3. 先理解已经确认的官方事实:
   - EP 0x03 是命令 OUTEP 0x82 是响应 INEP 0x81 是帧流。
   - 帧 marker 是 0x1BB1B11B,像素从帧内 +0x1c 开始,160x120 uint16 LE。
   - f20 平滑器 dev+0x41f10 当前 mode=1,正常帧基本直接复制,不是隐藏 EMA。
   - ref 在 dev+0x41ef0,启动/FFC 平滑器 mode=4 形成,正常显示帧不逐帧重采集。
   - NUC 0x180017200 使用 d2=(f20-ref)>>1、每像素两个 signed threshold、三段 gain/off、Q12 乘法和 clamp。
   - 0x180017330 是固定盲元邻域补偿,不是全帧 3x3 blur。
   - 0x18001e920 当前 mode=1 通常跳过,不要假设官方有开启的 temporal filter。
   - 官方 gain/off/threshold 表由传感器温度、DDT 配置和 FFC 重建生成,不读取 scene/live/ref。

4. 当前 demo3 的真实状态:
   - 已接入一组官方活体抓取的静态 gain/off/threshold 表。
   - 用输出域中值反求全局 ref bias。
   - SBNUC 只允许曾经热、后来负残差的像素自愈;低温从头到尾的负残差不应更新参考。
   - 显示层 deghost_nuc() 不调用,BADMAP_ENABLE=0,窗口、LUT、palette 不要随意改。
   - 低温区域长时间停留仍有残影,说明问题尚未解决。

5. 当前首要研究目标:
   - 查清低温静止目标的残影是在 raw/live、ref、d2、官方 gain/off、盲元补偿还是窗口前产生。
   - 不能只看 BMP;必须逐像素保存和比较 live/f20/ref/d2/raw/gray。
   - 建立官方和 demo3 的 60 帧以上同场景数据集,覆盖热目标和冷目标。
   - 复刻官方 gain/off/threshold 温度端点插值和 FFC 后表重建。
   - 复刻固定盲元补偿记录表,不能用场景动态 badmap 代替。

6. 已经失败、不要盲目重复的路线:
   - 显示层 motion history、recent hot mask、邻域替换。
   - 单纯把 SBNUC 全局 alpha 调快。
   - 把所有负残差都快速写回参考。
   - 把所有正负残差都用一个 MOG 参考吸收。
   - 随意修改窗口、调色板、FFC 周期和 BADMAP_ENABLE。

7. 修改原则:
   - 先保留当前已验证的官方查表、窗口、LUT、palette 和 USB 链路。
   - 只在有反汇编或抓帧证据的地方修改。
   - 不回退用户已经确认有效的修复。
   - 所有手工编辑使用 apply_patch。
   - 修改后使用用户给出的 GCC 命令编译,并启动 demo3 让用户实测。
   - 不要声称“已修复”,除非有用户实机反馈或可复现的同帧数据证据。

8. 编译命令:
   gcc -O2 -w -DMAG160C_STATIC -I"C:\Project\MAG160C\csdk\third_party\libusb\win64" -I"C:\Project\MAG160C\csdk\include" -I"C:\Project\MAG160C\csdk\src" -o "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" "C:\Project\MAG160C\csdk\tools\mag160c_demo3.c" "C:\Project\MAG160C\csdk\src\mag160c_display.c" "C:\Project\MAG160C\csdk\src\mag160c_error.c" "C:\Project\MAG160C\csdk\third_party\libusb\win64\libusb-1.0.x64.a" -lgdi32 -luser32

9. 启动命令:
   Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
   Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"

10. 你的第一条回复不要直接提方案。先返回:
   - 你读过的文件。
   - 当前官方链路理解。
   - 当前 demo3 链路理解。
   - 低温残影的证据缺口。
   - 下一轮最小可验证实验。
然后再开始修改或抓帧。

16. 交接结束条件

下一位接手者在以下条件都满足前,不应把项目标记为完成:

  1. 官方和 demo3 的 60 帧以上同帧 raw/ref/NUC 对比完成。
  2. 热目标和冷目标的进入、停留、离开都没有明显残影。
  3. FFC 前后表重建、窗口和显示冻结行为与官方一致。
  4. 固定盲元补偿记录已复刻,异常零点和少量极端 outlier 消失。
  5. 不依赖显示层遮罩,不牺牲真实目标,不产生黑洞。
  6. 用户在实机上确认低温和小温差场景都达到可接受效果。