# MAG160C 逆向交接文档 v2(2026-08-11) > **本文件是当前最新、最完整的上下文快照。** 覆盖硬件/协议/官方SDK逆向/ > 显示管线演进/全部失败教训。下一轮请从第 1 节开始读,严格按第 7 节 > 的方法论执行。 ## 1. 项目目标 对 Elo/Magnity MAG160C USB 热成像相机(160x120, 15fps)完整逆向: 1. 官方从"USB 读取 raw → 显示到屏幕"的**完整管线**逐层复原 2. 自研 C SDK(csdk/)+ Windows demo 的**最终屏幕画面必须与官方 app 一致** 3. Linux 移植(最终目标) ## 2. 硬件与协议(已验证,权威) ### 2.1 USB 拓扑 - VID 0x833C PID 0x0001,序列号 160043865,config 1,interface 0 - 端点:0x03 OUT(命令)、0x82 IN(命令响应)、0x81 IN(帧流)、0x84 IN(大读取,未用) - 驱动:libusb-win32 v1.2.6.0;传输库:TI libusb-1.0.dll + dlltool 导入库 ### 2.2 命令(已验证,与官方流量一致) | 命令 | 字节 | 响应 | |---|---|---| | 0x6bb6b66b | 4 | 0x5bb5b55b 60B(pid@0, w=160@0x10, h=120@0x14, fps=15@0x18) | | 0x6bb6b66c | 4 | 0x5bb5b55c 60B(另一块,勿当 info) | | 0x6bb6b66f | 4 | 0x5bb5b55e 20B | | 0x6bb6b672 FFC | **8**(magic+param) | 0x5bb5b55f 4B | | 0x6bb6b673 START | 4 | 0x5bb5b55f | | 0x6bb6b674 STOP | 4 | 0x5bb5b55f(需先 clear_halt 0x03/0x82) | 启动序列:`66b → 66c → 66f → FFC(0)×2 → 300ms → START → 700ms` ### 2.3 FFC 语义与时序(已精确解码,稳定方案已验证) - FFC(1):流切换为 **type=0**(温度数据,手掌 +1200 counts) - FFC(0):流切换为 type=1(raw) - 官方 cadence:FFC(0)→ 恰 9 帧 → FFC(1)→ 间隔 34~2680 帧 → 循环 - **已验证稳定方案**:period=400 帧 + gap=9 帧(FFC 必须在完整帧后发) - 实测:1400+ type=0 帧 / 15.1fps / 0 停帧(多次) ### 2.4 帧格式(重要:像素偏移!) ``` 28B 头: 0x1bb1b11b | cnt | len=0x9600(38400) | type | shutter | 保留 38400B: 像素 u16 LE 28B 尾: 0x1bb1b11c @ offset 38400 | 统计值(如 0x7839) ``` **关键:第二次 bulk read 返回 38428 字节 = 38400 像素(从偏移 0 开始)+ 28 尾。 demo 曾用偏移 +28 读像素是错的(错位 14 u16)。** 正确:像素从 data[0] 开始。 ## 3. 实机数据(决定性事实) ### 3.1 counts 数据特征 - 背景 counts 均值 ~11224(会话间 11224~13431 漂移) - **巨大固定 mura**:空间 std 3560,行跨度 15000+ (row0 ~17318 → row120 ~7954,row119 回升;两个会话剖面几乎相同) - row1 会话内恒定(+5000),漂移 62~1535(FFC 状态不同) - **每像素时域噪声 std ~248 counts**(99.9% 像素波动,非坏点) - 坏点:min=0/max=45340 恒定出现的像素(少数) ### 3.2 NUC 验证(关键结论) - `live - (ref - mean(ref))`:mura std 3560 → **29**,行跨度 15000 → 23~49 - NUC 后温度(官方 T2E shift=6):**24.9°C 均匀(std 0.19)**,与官方探针 26.7~27.4°C 接近 - **结论:NUC 平场校正是消除 mura 的正确方法** ## 4. 官方 SDK 逆向成果(本会话最大收获) ### 4.1 可活体驱动的官方管线 Windows 官方 `CoreSDKLib.dll`(490KB, 65 导出,精确 RVA 已解析)+ `ThermalSDK.dll`(90KB, 19 导出)。**ThermalSDK 高层 API 可直接驱动设备**: ```c // 已验证可用的调用(tsdk_debug/tsdk_capture3/tsdk_gray/tsdk_palette): SetDllDirectoryA("C:\\Project\\MAG160C\\IR_Camera_SDK-1.0.1\\windows\\windows\\app"); LoadLibraryA("ThermalSDK.dll"); // 依赖同目录 CoreSDKLib.dll 等 SetUnitMode(0); SetTempBoundary(30,44,37); SetNewIRFrameDelegate(onIR); Start(); // 内部:NewChannel(0) -> EnumCameras -> LinkCamera(chan,pid) -> StartProcessImage // 回调:onIR(buf, 320, 240, 3) 拿到官方渲染 RGB 帧(2x 放大) // ReadTemperatureAtPoint(x, y, res) -> 16B 结构, tempRaw=double @16, tempArm=double @24 ``` ### 4.2 已提取的官方数据(analysis/ 目录) | 数据 | 位置 | 说明 | |---|---|---| | 官方 T2E 表(646 int32) | `analysis/official_t2e_table.txt` | 从 .rdata 0x5cde0 提取,单调递增 51→4200129 | | 官方渲染帧 ×8 | `build-artifacts/official_ir_00..07.rgb` | 320x240 RGB,品红系 | | 官方灰度图 | `build-artifacts/official_gray.bin` | 160x120,93% 像素=0,顶部14行坏点带 | | 官方调色板(错误读取) | `build-artifacts/official_palette_live.bin` | Jet 色系 BGR,疑似偏移错误,勿用 | | 官方调色板(帧反推 115色) | `analysis/temp/official_palette_full.npy` | 品红系,可信 | | 官方探针温度 | 记录在 handoff | 背景 26.7~27.4°C | | 官方 T2E 表(已转C头) | `csdk/src/mag160c_official_t2e.h` | 646 项 | | 官方调色板(已转C头,插值) | `csdk/src/mag160c_official_palette.h` | 256 项,品红系(可能需修正) | ### 4.3 官方温度公式(0x180016290 反汇编,权威) ``` x = counts << (7 - shift) ; shift 参数运行时确定(本机验证=6) i = 二分查找 T2E[i] <= x < T2E[i+1] diff = x - T2E[i] slope[i] = (0x1000000 + (T2E[i+1]-T2E[i])/2) / (T2E[i+1]-T2E[i]) temp_mc = (slope[i]*diff >> 12) + (i<<12) - 0x249f0 ; 毫°C ``` 验证:counts 11224 → shift=6 → 24.82°C;12500 → 31.83°C;13000 → 34.48°C NUC 后背景 → 24.9°C(均匀)。 ### 4.4 官方渲染机制(部分逆向) - `MAG_GetOutputBMPdataRGB24(chan, buf, size, order)`:8-bit 灰度 `[dev+0xb00]` → 调色板查表(256×4 BGR)→ 输出 320x240 RGB - `MAG_GetOutputBMPdata(chan, w, buf)`:返回灰度缓冲指针 `dev+0xaf0`(19200B) - **未逆向:counts → 灰度(dev+0xaf0)的生成逻辑**(AGC/温度窗口在哪) - **未确认:调色板确切偏移**(dev+0xb00 vs graybuf+0x10 读到的数据不符) ### 4.5 官方显示效果(用户标准) - 官方 app 画面:背景**品红(145,0,145)**,热物体紫红渐变,顶部坏点带白色 - 官方灰度:93% 像素=0(说明显示窗口下限 > 背景温度,背景灰度≈0? 但渲染背景是品红 → 矛盾,需重新验证灰度与渲染的对应!) - **注意:official_gray.bin 与 official_ir_03.rgb 是不同会话抓的, 不能直接配对!必须同一时刻抓灰度+RGB+温度** ## 5. 显示管线演进与失败教训(重要) | 版本 | 方案 | 结果 | 教训 | |---|---|---|---| | v1 原始 | 直接显示 counts + percentile | 乱图/坏点 | raw 不可直接显示 | | v2 | EMA 参考 + diff | 鬼影 | EMA 吸收静态场景 | | v3 | 全局漂移 + 坏点拓扑填充 | 鬼影仍在 | 参考吸收小物体 | | v4 | MOG 冻结 + 自愈 | 乱图叠加 | 参考采集被 quiet-gate 卡住 | | v5 | MOG 冻结 + FFC 重采集 | 画面更混乱 | 参考从未建立(NUC 未激活) | | v6 | NUC 平场 + 自适应窗口 | 平滑但对比度差、颜色不对 | 找到了 NUC,但窗口/调色板错 | | v7 | NUC + T2E + 官方调色板 | 品红背景与官方一致但**什么都分辨不出** | 窗口/灰度映射仍不对,对比度丢失 | **用户最新反馈(2026-08-11)**:v7 画面"什么都分辨不出来"(对比度丢失, 物体与背景无法区分),且"还是有鬼影"。结论:即使色系对了,灰度映射/ 窗口机制仍然错误,参考帧方案仍有残留问题。**必须回到官方完整管线**。 ### 5.1 核心失败教训 1. **不能边猜边试**:v1-v7 都是"猜显示算法→实机看效果→再猜"。 用户明确要求:**先把官方"读取→显示"全流程逆向完毕,再改代码**。 2. **最终屏幕画面必须与官方一致**:用户以此为标准,不是"看起来平滑"。 3. **参考帧/NUC 的副作用(鬼影)未解决**:NUC 参考冻结后无更新, 温度漂移会导致残留。 4. **官方灰度图与渲染帧未配对**:不同会话数据不可用于像素级对比。 5. **调色板偏移未确认**:graybuf+0x10 读到的 Jet 色系与渲染帧品红系矛盾, 说明读错位置或存在多个调色板。 ## 6. 当前代码状态 - `csdk/tools/mag160c_demo2.c`:最新版(官方管线尝试,v7) - 显示:counts → NUC → T2E temp(shift=6)→ 自适应窗口(背景±2°C)→ 品红调色板(插值版) - 已知问题:对比度差(分辨不出物体)、可能有鬼影 - `csdk/tools/mag160c_ffc_test.c` / `csdk_stream_test.exe`:FFC 稳定性验证(已 PASS) - `csdk/tools/tsdk_*.c`:官方 ThermalSDK 活体 harness(tsdk_debug 最稳定) - `csdk/src/mag160c_display.c`:纯 C 显示模块(NUC/坏点/AGC/FFC 调度) - `csdk/src/mag160c_official_t2e.h` / `mag160c_official_palette.h`:官方表 - `csdk/tests/test_display.c`:32/32 通过 - 证据图:`analysis/demo2_v2..v7_*.png` ## 7. 下一轮方法论(必须遵守) ### 7.1 总原则 **先把官方"读取→显示"全流程逆向完毕并像素级对比一致,再动 demo 代码。** ### 7.2 具体步骤(建议顺序) 1. **同帧抓取**:写一个 harness(基于 tsdk_debug),在**同一帧**内抓: - 官方灰度(MAG_GetOutputBMPdata → dev+0xaf0) - 官方渲染 RGB24(MAG_GetOutputBMPdataRGB24) - 官方温度(MAG_GetTemperatureData 或 ReadTemperatureAtPoint 多点多帧) - 官方调色板(需先确认正确偏移) 存成配对文件(如 official_pair_%03d.gray/.rgb/.bin)。 2. **逆向 counts→灰度 生成代码**:在 CoreSDKLib.dll 找写 dev+0xaf0 的 函数(帧处理线程),确认:AGC 算法(线性?直方图?)、温度窗口、 是否含 NUC/坏点。这是"读取→显示"缺失的核心环节。 3. **确认调色板**:用同帧灰度+RGB 精确提取 256 项调色板 (gray→color 逐项),替换现在的插值版。 4. **像素级对比**:用我们的 counts + 逆向的完整管线重建画面,与 官方同帧渲染逐像素对比(相关系数、色差),迭代到一致。 5. **温度标定**:用官方温度数据校准 shift 和 offset,替换 147 counts/C。 6. **鬼影**:参考帧方案在官方管线确定后重新设计(官方是否有参考帧? 还是纯逐帧处理?)。 ### 7.3 工具与入口 - 设备空闲检查:`Get-PnpDevice | ? InstanceId -match 833C` - 官方 harness:`build-artifacts\tsdk_debug.exe`(最稳定,先跑这个验证设备) - 我们的帧 dump:`build-artifacts\mag160c_frame_dump.exe ` - 分析脚本:`analysis/temp/`(Python,numpy/scipy/PIL 可用) - 官方 DLL 反汇编:objdump -d -Mintel(RVA 见 `analysis/temp/exports2.py`) ### 7.4 环境 - Windows 11 + MinGW gcc 14.2(`C:\mingw64\bin`),无 cmake - libusb:`csdk/third_party/libusb/win64/` - 官方 SDK:`IR_Camera_SDK-1.0.1/windows/windows/app/` - Python 3.10 + numpy/scipy/PIL