Files

192 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 <n> <dir>`
- 分析脚本:`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