chore: reorganize analysis/ by SDK lineage (linux/win/android) with README index, move legacy notes to history/, refresh root README, ignore .zcode/

This commit is contained in:
ZXCLI
2026-09-10 03:09:13 +08:00
parent 4ba98f62cd
commit e69a97436b
137 changed files with 123 additions and 62 deletions
+416
View File
@@ -0,0 +1,416 @@
# MAG160C 项目交接文档 (2026-08-10)
> **2026-08-10 晚更新(本会话)**:FFC 停帧问题已解决并多次实机验证;
> 坏点/对比度/标定管线已按开源方案重做并合入 csdk。详见第 10 节。
## 1. 项目目标
对 Elo/Magnity MAG160C USB 热成像相机进行完整逆向,实现一个独立、可用的
Linux/Windows C SDK + Windows GUI Demo。已完成的逆向依据:
- `analysis/protocol_spec.md`(协议全谱)
- `analysis/findings.md`(证据链)
- `analysis/captures/libusb0_trace.txt`(官方 demo 完整 USB 流量,26092 行)
- `analysis/captures/official_hand.pcap`(官方 demo 手掌遮挡抓包)
- `analysis/captures/csdk_verified.pcap`(csdk 抓包)
## 2. 硬件事实(实机确认)
- 设备名:Magnity Thermal Camera,VID 0x833C PID 0x0001,序列号 160043865
- 分辨率 160x120,帧率 ~15fps,每帧 38456 字节
- USB:config 值 1(仅一个配置),interface 0,端点:0x03 OUT、0x81/0x82/0x84 IN
- 设备只有 config 1——Linux SDK 逆向中的 set_configuration(2) 在这台机器上失败,
会回退到 1(逆向代码 0x1865b 分支)
## 3. 已验证协议(权威,实机+官方流量双重确认)
### 3.1 命令(4 字节魔数,无参数;仅 FFC 用 8 字节 {magic,param})
| 命令 | 字节 | 响应 |
|---|---|---|
| 0x6bb6b66b 信息查询 | 4 | 0x5bb5b55b(60B:pid@+4,width=160@+0x14,height=120@+0x18,fps=15@+0x1c) |
| 0x6bb6b66c | 4 | 0x5bb5b55c(60B,另一数据块,勿当 info 解析) |
| 0x6bb6b66f | 4 | 0x5bb5b55e(20B) |
| 0x6bb6b672 FFC | 8 | 0x5bb5b55f(4B OK) |
| 0x6bb6b673 START | 4 | 0x5bb5b55f |
| 0x6bb6b674 STOP | 4 | 0x5bb5b55f(需先 clear_halt 0x03/0x82) |
注意:帧头字段偏移以"帧内"为准:marker@0、cnt@4、len@8、type@0xc、shutter@0x10
### 3.2 启动序列(官方一致)
```
open(config 1, claim iface 0)
66b → 66c → 66f (各 4B)
FFC(0) → 100ms → FFC(0) → 300ms → START(4B) → 700ms
```
### 3.3 FFC 语义(关键难点)
- FFC(1) 使流切换为 **type=0 帧(温度数据,avg ~11720,手掌遮挡 +1200 counts)**
- FFC(0) 使流切换为 type=1 帧(avg ~16000,手掌遮挡变化小 <100)
- **官方 demo 在 type=0 下每 ~10 帧发一次 FFC(0/1 交替)维持推流**
- FFC 必须在"完整帧(头+数据)之后"发送
- 问题:在 libusb-1.0(TI DLL)或 libusb0 下,FFC(1) 切换后设备经常停帧;
`csdk/tools/thermal_viewer.c`(单线程、frames==3 时 FFC(1)+Sleep(400))
能稳定工作(已验证多次,显示 type=0 帧 100+)
- `csdk/tools/mag160c_demo2.c` 基于 viewer 核心,但被强杀/反复运行后设备
进入坏状态需 libusb_reset_device 重试(已加 4 次重试)
### 3.4 帧格式
```
+0x00 u32 0x1bb1b11b
+0x04 u32 frame counter
+0x08 u32 0x9600 (38400 data length)
+0x0c u32 type (0=温度/response, 1=raw)
+0x10 u32 shutter
+0x14..0x1b 保留
+0x1c .. 38400 字节像素 (u16 LE)
+0x1c+38400 u32 0x1bb1b11c (尾标记)
之后 24 字节尾字段(含 0x6cf0 类统计值)
```
## 4. 温度数据(实测)
- type=0 帧:背景 avg≈11720,手掌遮挡(整个 FOV)+1200 counts
- 线性标定:约 **147 counts/°C**(假设手掌 34°C vs 背景 25°C,未精确标定)
- demo 温度显示:25 + (v - reference)/147
- 官方 Android SDK 有 T2E 表(0x112 项,已 dump 到 analysis/t2e_table.json)
和 E2TAccQ10 表(逆向的 ReviseTemperature/CorrectTemperature 用)
## 5. 当前 Demo 状态(csdk/tools/)
| 文件 | 状态 |
|---|---|
| `thermal_viewer.c` | ✅ 稳定工作:单线程循环、frames==3 时 FFC(1)、percentile 2%-98% 渲染、type=0 显示 |
| `mag160c_demo2.c` | 🔶 基于 viewer 核心,加面板/按钮/温度/坏点检测;有设备状态残留问题(需 reset 重试) |
| `mag160c_demo.c` | ❌ 弃用(自行写的 init/FFC 逻辑设备会停帧) |
| `mag160c_demo_usb0.c` | ❌ 弃用(libusb0 API 版,同样停帧——问题不在传输层) |
### 已知显示问题(需参考开源实现解决)
1. **坏点(红蓝固定点)**:type=0 帧存在固定坏像素,显示为红/蓝点。
已在 demo2 加"参考采集时 min-max 波动 >400 标记坏点、邻居平均替换",
但用户反馈仍可见(阈值/方法需优化,或坏点表应在 FFC 后重新采集)
2. **对比度**:diff 模式(当前-参考,deadband±70,span 1500)手掌轮廓可见但偏弱;
absolute 模式 percentile 拉伸对比度差
3. **FFC 后设备停帧**:不稳定,与官方 demo(持续工作)行为不一致,
差异可能在于官方 FFC 的精确时序/频率
## 6. 环境与工具
- Windows 11,MinGW gcc 14.2 (C:\mingw64)
- 设备驱动:**libusb-win32 v1.2.6.0**(Zadig 安装;WinUSB 也可但 TI libusb DLL 的
FFC 写失败;官方 libusb-1.0.27 在两种驱动下都崩溃)
- 传输库:**TI libusb-1.0.dll**(C:\ti\ccs2050\ccs\ccs_base\common\bin\,
支持 libusb0 后端,有无害的 monotonic clock 警告)
+ dlltool 生成的导入库 csdk/third_party/libusb/win64/
- USBPcap 1.5.4 + Wireshark(重启后可用,设备在 \\\\.\USBPcap4)
- 抓包 pcap 解析脚本:analysis/captures/ 下(参考之前会话)
## 7. 开源热成像 SDK 参考(下一轮调研目标)
网络在部分时段可用,建议用 webfetch 搜索:
- **FLIR Lepton SDK**(github: groupgets/LeptonModule 或 purethermal1):展示了
raw→radiance→温度、AGC(自动增益控制)、伪彩色流程;坏点补偿思路
- **Seek Thermal 开源驱动**(github: ThermalScope/seek-thermal-camera 或
fnoop/seek-thermal-linux):EEPROM 坏点表、NUC(非均匀性校正)
- **MLX90640 库**(github: intergatedcircuits/Pico-MLX90640):插值+显示
- **AMG8833/MLX90640 的 Adafruit 库**:显示映射方法
- 关键要学习:①坏点检测与补偿算法 ②AGC/显示拉伸(直方图均衡 vs percentile)
③NUC/FFC 后的温度图管线 ④伪彩色 LUT 设计
## 8. 下一步工作
1. 调研开源热成像显示管线,改进 demo2 的:坏点补偿、AGC(显示拉伸)、伪彩色
2. 稳定 FFC:对照官方 trace 精确复刻 FFC 时序(包括周期性),解决停帧
3. 温度标定:用已知温度(冰水/热水)做两点标定,替代 147 counts/°C 假设
4. 把验证过的协议与显示管线合并回 csdk 的 mag160c_ir 实现
5. Linux 移植(最终目标)
## 9. 快速启动命令
```powershell
# 设备恢复(设备停帧时)
.\build-artifacts\usb_hardreset.exe
# 运行 demo(能看手掌的稳定版)
.\build-artifacts\thermal_viewer.exe
# 功能完整版(重试+坏点+新 FFC 调度)
.\build-artifacts\mag160c_demo2.exe
# 无头 FFC 稳定性验证(1400 帧 type=0,~95s)
.\build-artifacts\mag160c_ffc_test.exe
# csdk 线程管线验证(reader thread + FFC scheduler)
.\build-artifacts\csdk_stream_test.exe
# csdk CLI 综合(显示管线自检 / 流验证)
.\build-artifacts\csdk_cli_libusb.exe display-test
.\build-artifacts\csdk_cli_libusb.exe ffc-test 1000
# 抓包
& "C:\Program Files\USBPcap\USBPcapCMD.exe" -d \\.\USBPcap4 -o out.pcap -A -s 65535
```
## 10. 2026-08-10 晚:FFC 停帧解决 + 显示管线重做(本会话)
### 10.1 官方 FFC 时序(从 libusb0_trace.txt 精确解码)
逐行统计 26092 行官方流量,每帧 = 28B 头 + 38428B 数据。FFC 事件与帧计数:
```
init: 66b 66c 66f -> FFC(0) -> START -> [frame 1] -> FFC(0)
frame 10: FFC(1) -> 流切换 type=0(第 11 帧起)
然后周期循环: FFC(0) --9 帧--> FFC(1) --N 帧--> FFC(0) ...
```
- **FFC(1) 总是在 FFC(0) 后恰 9~10 帧**发出(0.6s@15fps,即快门闭合→恢复)。
- FFC(1) 后到下一个 FFC(0) 的间隔 N 不固定:34, 48, 66, 101, 166, 373, 346,
103, 1383, 381, 48, 1006, 317, 171, 158, 189, 219, 294, 425, 628, 723,
1012, 1450, 1804, 2680, 403, 105 …(用户/定时器驱动,非固定周期)。
- 全程 10898 帧持续推送,type=0 占 10519 帧(96.5%),type=1 只出现在每个
FFC(0)→FFC(1) 的 9 帧窗口。
- **FFC 必须在一整帧(头+数据)读完之后发送**(trace 中 FFC 行紧跟帧数据行)。
- **教训**:demo2 旧版只发一次 FFC(1) 且周期 44 帧过密 → 设备停帧或 type=1
占比过高(17.7%)、fps 下滑。官方式 **周期 400 帧 + 9 帧间隔** 完美工作。
### 10.2 实机验证结果(全部 PASS)
| 验证项 | 工具 | 结果 |
|---|---|---|
| FFC 周期 400 + gap 9 | `mag160c_ffc_test.exe` | 1400 type=0 帧 / 95s / 15.1fps / 0 停帧(2 次) |
| csdk 线程管线(reader thread + scheduler) | `csdk_stream_test.exe` | 1030 type=0 帧 / 70s / 14.7fps / 0 停帧 |
| csdk CLI `ffc-test` | `csdk_cli_libusb.exe` | 同上,PASS |
| 显示管线单测 | `csdk_test_display.exe` | 20/20 通过 |
| GUI demo2 | `mag160c_demo2.exe` | 窗口实时显示 type=0,坏点红蓝占比 0.02%,无饱和坏点 |
| 回归(旧 4 套) | csdk test_* | 全部通过 |
### 10.3 显示管线(按开源方案重做,已合入 csdk)
新增 `csdk/src/mag160c_display.c` + `csdk/include/mag160c/mag160c_display.h`,
纯 C 无平台依赖(Linux 可直接编):
1. **坏点检测**(参考 OpenThermal/libseek-thermal SeekCam.cpp + MLX90640):
- 时域:参考帧 min-max 波动 > 400 → 坏点(保留旧法)
- 直方图峰偏离(Seek 法):`thr = histPeak - (frameMax - histPeak)`,
并加 `histPeak + 200` 下界防孤立极值误杀全图
- **拓扑序填充**:坏簇从边缘向中心迭代,存填充顺序;参考图与每帧
都按该顺序用 4 邻域均值替换(坏簇逐层内缩)
2. **AGC/显示拉伸**:
- 绝对模式:2%~98% percentile 拉伸 + **ironbow LUT**(FLIR 风格 7 锚点)
- diff 模式:自适应 span = 2×mean|diff|,clamp [120, 4000],
deadband = span/20(轮廓对比度显著提升)
3. **FFC 调度器** `mag160c_ffc_scheduler_t`:tick(帧后)返回应发的 FFC
参数(0/1/-1);`mag160c_ir_set_ffc_scheduler()` 挂到 reader thread,
Linux 线程模型直接可用。
4. **两点线性标定** `mag160c_temp_calibrate_linear(counts0,temp0,counts1,temp1)`
→ a/b;demo2 有 Cal Cold(0°C)/Cal Hot(90°C) 按钮,CLI 有 `calibrate` 占位。
### 10.4 遗留事项(下一步)
- **物理两点标定**:需实际操作(冰水 0°C / 开水 90°C+ 充满 FOV),用 demo2
的 Cal Cold/Cal Hot 按钮或记录 counts 代入 `mag160c_temp_calibrate_linear`
当前仍是 147 counts/°C 假设(参考背景 ~11720 counts @ 25°C)。
- demo2 内联的坏点/AGC 逻辑与 csdk 模块功能重复,后续可让 demo2 直接调
csdk 模块(现 demo2 已用 csdk 的 FFC scheduler)。
- Linux 移植:csdk 侧已就绪(纯 C + pthread + libusb);GUI demo 的 Win32
部分需换 SDL/GTK。
### 10.5 后续小 bug 修复(2026-08-10 深夜)
用户反馈两个现象,均已修复并实机验证:
1. **鬼影**:旧参考帧用逐像素 EMA(`(r*199+v)/200`)持续吸收当前画面,
静止物体 ~13s 就被"吃进"参考帧,移开后留下反向残影。改为**漂移跟随**:
仅当场景安静(mean|diff| < 40)时,把参考帧整体平移全局均值差
(clamp ±400),不再吸收任何空间内容 —— 跟温度漂移/FFC 基线,不跟场景。
2. **FFC 响应红黄闪烁 + 卡顿**:FFC(0) 后设备输出 ~9 帧 type=1(avg ~16000,
与 type=0 基线差 +4300),旧 demo 照常渲染 → 全屏红黄;手动 FFC 按钮在
UI 线程 `Sleep(300)` 阻塞。修复:
- 主循环**跳过非 type=0 帧的渲染**(冻结画面,与官方 app 一致)—— 实测
218s/3744 帧/~8 次自动 FFC 周期标题栏从未出现 type=1,闪烁消除。
- 手动 FFC 改**异步排队**:按钮只置 `g_manual_ffc` 标志,主循环在下一
完整帧后经 `mag160c_ffc_scheduler_trigger()` 发 FFC(0),9 帧后自动
发 FFC(1),UI 不再阻塞。
- csdk 新增 `mag160c_ffc_scheduler_trigger()`(test_display 22/22 通过)。
### 10.6 二次修复:鬼影根因 + FFC 残闪(2026-08-10 深夜,实机验证)
用户复测:鬼影仍存在,且高温物体进入鬼影区会被"遮住"成低温色;
红黄帧不再卡住但偶尔闪过。根因分析 + 修复(参考 OpenCV MOG 背景建模
与 Seek 挡片校准思路):
1. **鬼影根因**(两层):
- 旧参考更新用"全局 mean drift":物体占 FOV 比例小时 mean|d| < 40
误判安静,全局偏移把**小物体逐步吸收进参考帧**(数秒~数十秒),
移开后留下高温残影;真实高温物体移到该位置时 diff=live-ref 变负
→ 显示成低温,即"被遮住"。
- 初始参考采集是 30 帧**均值**:采集窗口内若有物体在动,均值会把
物体烘焙进参考,永久鬼影。
- 修复:
a. **MOG 逐像素背景模型**(`mag160c_display_ref_track`):背景像素
(|d| < 60)慢速 EMA 跟踪漂移;前景像素(|d| >= 60)**完全冻结**,
永不吸收(之前是 clamp+漏吸收,约 112 counts/s 泄漏)。
b. **安静门控的初始采集**:每帧与上一帧 mean|Δ| < 25 才入样本,
30 帧逐像素取**中位数**(物体短暂进入窗口不会污染参考);
场景一直不静则参考一直不落(可手动 Set reference)。
c. 合成测试 `MOG_GHOST_TEST`:静态 +1200 热块 4500 帧(模拟 5 分钟)
吸收 0 像素;移开后 diff≈0 无鬼影;慢漂移仍可跟踪 → PASS。
2. **FFC 残闪**:FFC(1) 后 type=0 基线跳变(官方 trace 实测最大 +1000
counts),旧重对齐 ±400 clamp 不够,残余偏移需 MOG 数秒才消化 →
短暂偏红。修复:重对齐 clamp 放宽到 ±2000(中位数本身抗 >50% FOV
物体),FFC(1) 后跳过前 2 帧渲染(`g_skip_ffc`)。
实测连续 3+ 个 FFC 周期(period 400)红色占比前后无跳变(11.1% →
11.1%),蓝色始终 0.01%,type=1 帧从不渲染,流不断(1500+ 帧)。
证据:`analysis/demo2_v3_mog_verified_20260810.png`;单测 28/28 通过
(`csdk_test_display.exe`),旧 4 套回归全绿。
### 10.7 三次修复:开机乱图 + 残余鬼影(2026-08-10 深夜,实机验证)
用户复测:FFC 红黄闪帧已解决,但(1) 刚开软件画面有红黄点乱分布,
且不会消失、叠加到正常画面上;(2) 鬼影仍存在。
**根因**(两层,均为 MOG"永久冻结"缺陷):
1. **开机乱图**:参考帧首次采集在 nread>=150(~10s,第一个自动 FFC 周期
之前)进行。设备刚完成启动 FFC(1),type=0 基线/坏点尚未稳定,启动
噪声(红黄点)被中位数采入参考;之后 live 已正常,这些像素
|d| >= 60 被 MOG **永久冻结** → 乱图叠加且永不消失。
2. **鬼影**:同上——参考一旦采错(含物体/含噪声),MOG 冻结使其无法自愈;
FFC 后一次性 median rebase 只平移全局,修不掉逐像素错误。
**修复**:
1. **首次参考采集提前到启动 FFC(1) 稳定之后**(nread >= 40 + 跳过 2 帧
过渡),不在第一个自动 FFC 周期前采集 → 启动噪声不再进入参考。
2. **每次 FFC(1) 后重新采集参考**(REF_REINIT_AFTER_FFC):FFC 校准后
设备输出最干净的 type=0 帧,用它重建参考(安静帧中位数,30 帧,
沿用坏点表填充),取代一次性 rebase。每 ~27s 自动刷新一次,任何
残留错误最长一个周期内被清除。
3. **MOG 自愈**(`mag160c_display_ref_track_heal`):孤立像素(8 邻域前景
< 2)连续前景 >= 30 帧 → 判定为参考错误(启动噪声/坏点)而非物体
(物体是连片的),重置 ref = live。乱图/残点最长 ~2s 内自愈。
**验证**:
- 合成测试 `SELFHEAL_TEST` / csdk `test_display` 30/30:40 个孤立噪声点
全被治愈;60x40 连片热块 200 帧吸收 0 像素、不被误治;移开后无鬼影。
- 实机:开机 t=4s 孤立噪声 0.12%,t=34s(参考+FFC 后)0.11%,t=90s
(5+ FFC 周期)0.10% —— 乱图不出现、不叠加、不增长;红色像素全部为
真实场景连片目标(45~71 个大 blob);FFC 周期红%无跳变(6.4~8.6%),
蓝%恒 0.01%;流持续 2157+ 帧无停帧。
证据:`analysis/demo2_v4_selfheal_verified_20260810.png`
### 10.8 四次修复:根因定位(传感器 mura)+ NUC 平场校正(2026-08-10 深夜,数据驱动)
用户复测:开机乱图和鬼影依然存在。本次用数据驱动定位**真正根因**,并
采用开源(Seek/FLIR 挡片帧)同款方案修复。
**实测数据(mag160c_frame_dump 抓 60 帧 type=0 分析)**:
- 每像素时域噪声 std ~248 counts(99.9% 像素"波动")→ diff 模式自适应
span≈400、deadband≈20 把噪声全部放大成可见红蓝点 = **开机乱图**
- 固定传感器 mura:行剖面跨度 15594(row1 恒定 +5000,25418 vs row98 9824)、
列剖面 8714;FFT 行/列轴能量占比 ~10%
- mura 会话内漂移 ~350 counts,FFC 后突变 → diff 参考帧跟不上 = **鬼影**
- flat-field 校正验证:`live - (ref - mean(ref))` 把 mura std 3557→379
(行跨度 15704→401),中值滤波后噪声 ~80
**为什么官方 app 没有乱图/鬼影**:Android 官方管线是 raw→温度→**绝对
温度窗口显示(25~40°C)**,不用场景参考帧做 diff,天然免疫噪声放大与
参考漂移。
**修复(推翻 diff 方案,改 NUC 平场 + 绝对温度显示)**:
1. **flat-field(NUC)校正**(`mag160c_display_nuc`,csdk):`nuc = live -
(ref - mean(ref))`,消除固定 mura;显示绝对温度级别,median 居中
±1500 窗口 + ironbow。
2. **3x3 中值平滑**:把 248-count 时域噪声降 ~3 倍,画面平滑无噪点。
3. 参考帧只做**固定图案**,不跟踪场景:采集时 5% 像素偏离阈值即拒帧
(持久物体永不进参考)+ MOG 冻结 + FFC 后重采集 → 无鬼影。
4. diff 模式降级为可选(默认 absolute)。
**实机验证**:speckle residual 2.2~2.7(平滑)、纯蓝 0%、红色均为真实场景
连片目标(1~1.4%);连续 5+ FFC 周期无闪帧、无图案出现、无噪声增长;
流持续 2268+ 帧。csdk 单测 32/32,旧回归全绿。
证据:`analysis/demo2_v5_nuc_verified_20260810.png`。
工具:`csdk/tools/mag160c_frame_dump.c`、`mag160c_frame_stats.c`。
### 10.9 五次修复:数据驱动根因 + 官方 DLL 活体对比(2026-08-10 深夜)
用户复测:画面更混乱。本次彻底转向**官方实现对比**:
**逆向成果(活体官方管线)**:
1. 反汇编 Windows 官方 CoreSDKLib.dll + ThermalSDK.dll(65 个 MAG 导出,
精确 RVA 全部解析),确认官方渲染管线:
- `MAG_GetOutputBMPdataRGB24`:8-bit 灰度 `[dev+0xb00]` → 调色板查表
(256x4 BGR),输出 320x240 RGB
- `MAG_GetTemperatureData`:逐像素 counts→temp,T2E 表(646 项,已从
`.rdata 0x5cde0` 提取)+ 斜率表,公式:
`temp = slope[i]*diff>>12 + (i<<12) - 0x249f0`
- Windows 调用序列:NewChannel(chan) → EnumCameras(chan) →
LinkCamera(chan,pid) → StartProcessImage(chan)
2. **用官方 ThermalSDK.dll 活体驱动设备成功**(tsdk_debug/tsdk_capture3):
- 官方渲染帧 320x240,**紫红调色板(G=0,R/B 渐变),极平滑
(speckle 0.00%,仅 106 色),无 mura 无噪点**
- 官方温度:背景 26.7~27.4°C(ReadTemperatureAtPoint)
3. **数据对比锁定根因**:
- counts 有大尺度固定 mura:空间 std 3560、行跨度 15000+、
row0 17318 → row120 7954(固定,会话间一致,row1 漂移 1535/FFC)
- **官方图像完全无此 mura** → 官方用 per-pixel 校正(baseline/DDT)
消除了它
- **NUC 验证**:`live - (ref - mean(ref))` 把 mura std 3560→29
(行跨度 15000→23~49),mura 消除 99%
4. **demo2 的 bug**:参考采集的 quiet-gate(要求 mean|d|<25)在用户
观看时永不满足 → **NUC 从未激活** → 显示 raw counts 乱图。
**修复(最终方案)**:
1. 参考采集**去掉 quiet-gate**,直接取 30 帧逐像素**修剪中位数**
(中间 50% 均值,抗物体/死点)
2. **NUC 平场校正**为默认显示(绝对温度),消除 mura
3. **自适应窄窗口**:NUC 值 median ± 4*mean|d|(实测 std~29 → span~120,
手掌 +1200 清晰弹出),替代过宽的 ±1500 固定窗口
4. 3x3 中值平滑 + MOG 冻结/自愈 + FFC 后重采集(保留)
**实机验证**:unique colors 22260(高对比)、纯蓝 0%、孤立噪点 0.62%、
310 个大 blob(真实场景)、speckle 2.3、6+ FFC 周期无闪帧无鬼影累积、
流持续 2621+ 帧。csdk 单测 32/32 通过。
证据:`analysis/demo2_v6_nuc_final_20260810.png`。
工具:`csdk/tools/tsdk_debug.c`、`tsdk_capture3.c`、`official_harness.c`、
`analysis/temp/official_t2e.txt`(官方 646 项 T2E 表)。
### 10.10 六次修复:逐像素复刻官方显示管线(2026-08-10 深夜)
用户指出显示效果与官方不一致。通过**官方 DLL 活体驱动 + 逐层提取**,
完整复刻了官方的显示管线:
**逆向提取(全部来自官方活体)**:
1. **官方温度公式**(CoreSDKLib 0x180016290):counts → 646 项 T2E 表
二分查找 + 斜率插值:
`temp_mc = slope[i]*diff>>12 + (i<<12) - 0x249f0`
T2E 表已从 .rdata 0x5cde0 提取(646 int32,单调递增)。
**shift=6 验证**:counts 11224 → 24.8°C(与官方探针 26.7-27.4 一致,
差异来自 NUC/offset)。
2. **官方调色板**(从 8 帧官方渲染提取 115 色 + 插值到 256 项):
**品红/紫红系**:深红(74,0,0)→ 红 → 品红(145,0,145)→ 紫 →
蓝紫(115,32,210)→ 紫(154,0,188)。G 通道中段有小值。
3. **官方显示特征**:背景落在**调色板中段(品红)**,不是黑/深红——
因为显示窗口围绕背景温度(自适应),背景 ~25°C 映射到灰度 ~128。
**demo2 复刻(最终显示管线)**:
```
counts → NUC 平场(live - (ref - mean(ref)),消除 mura)
→ T2E 官方温度(shift=6)
→ 自适应窄窗口(背景温度 ± 2°C)
→ 官方品红调色板查表
```
**两个关键 bug 修复**:
1. 窗口单位 bug:temp_mc 是毫°C,窗口却用 °C 比较 → 全图灰度 255。
2. NUC 参考被 MOG 更新吸收背景 → NUC 失效(参考只 FFC 后重采集,
MOG 仅用于可选 diff 模式)。
**实机验证**:画面 mean RGB 139/25/144 vs 官方 141/1/130(几乎一致);
背景品红(139,0,117)与官方(145,0,145)同色系;黑色 0.1% vs 0.0%;
4+ FFC 周期无闪帧、稳定。csdk 单测 32/32。
证据:`analysis/demo2_v7_official_look_20260810.png`。
新增:`csdk/src/mag160c_official_palette.h`(256 项官方调色板)、
`csdk/src/mag160c_official_t2e.h`(646 项官方 T2E 表)、
`csdk/tools/tsdk_pair.c`、`tsdk_gray.c`、`tsdk_palette.c`。
+191
View File
@@ -0,0 +1,191 @@
# 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
+361
View File
@@ -0,0 +1,361 @@
# MAG160C 热像仪渲染项目交接 - 2026-08-11
项目目录: `C:\Project\MAG160C`
目标: 修复 `csdk\tools\mag160c_demo3.c` 的移动物体鬼影,同时不要破坏当前已经修好的画面问题。
## 1. 当前结论先说
- 黑洞问题已经基本消失。
- 固定遮蔽问题已经消失。
- 最高温点显示黑色的问题已经修掉。
- 当前主要剩余问题: 移动物体会留下鬼影,尤其是“小温差鬼影”擦除很慢。
- 官方 SDK 自己也存在类似“旧位置偏暗/偏黑”的残影,不是 demo3 独有问题。
- 现在最需要做的是: 在不破坏现有稳定画面的前提下,只针对“目标离开后的小残影”加速擦除。
## 2. 设备与构建
- 设备: MAG160C, 160x120, USB, VID `0x833C`
- 官方 SDK: `CoreSDKLib.dll`
- 当前主程序: `C:\Project\MAG160C\csdk\tools\mag160c_demo3.c`
- 输出程序: `C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe`
编译命令:
```powershell
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
```
重启命令:
```powershell
Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"
```
日志与抓图:
- `demo3_diag.txt`
- `demo3_auto_*.bmp`
官方抓帧:
```powershell
C:\Project\MAG160C\build-artifacts\tsdk_pair2.exe C:\Project\MAG160C\analysis\pairs_move 12 6000
```
注意:
-`tsdk_pair2.exe` 前要先停 `demo3`
- 如果报 `no channel`,通常要重插 USB
- 这个工具默认要给官方 SDK 几秒初始化时间,`6000ms` 延时是成功过的
## 3. 官方渲染管线逆向结论
已验证主链路:
```text
USB帧(type=0)
-> 平滑器 0x41f10 (模式1: 首帧 memcpy + EMA)
-> f20 (dev+0x41f20)
-> NUC查表 0x180017200
-> 输出到 dev+0x270
-> 窗口 hi/lo (dev+0x4202c / 0x42030)
-> gray (dev+0x220, 320x240)
-> palette 256
-> RGB
```
NUC 查表 `0x180017200` 已确认:
- `d2 = (f20 - ref) >> 1`
- 段阈值在 `dev+0x41858`
- gain/off 表在 `dev+0x41870`
- `shift = 12`
- 输出 clamp 到 `[0, 65535]`
参考帧 `ref`:
- 来自平滑器 `0x41ee0`
- 启动早期采集几帧后冻结
- FFC 后只做全局偏移,不重新采集整帧 ref
- `ref``live/f20` 同源 mura,能很好抵消固定纹理
FFC:
- 周期约 `1800` 帧,也就是约 `120s`
- 或者传感器温差变化超过阈值时触发
- `FFC(0)` 后约 `9` 帧再 `FFC(1)`
- `type=1` 期间官方会冻结画面
关键逆向修正:
- 原来以为 `0x180017330` 是整帧 3x3 平滑,这是错的
- 现在确认它更像“盲元/坏点补偿”,不是全帧模糊
- 所以 demo3 里已经去掉“整帧 3x3 blur”这条错误路径
## 4. 已经修好的问题
### 4.1 颗粒重
- 原因: NUC 斜率过大
- 修复: `NUC_GAIN=19`
### 4.2 FFC 后颜色跳变
- 原因: FFC(1) 后重采集 ref,把场景吸进参考
- 修复: `REF_REINIT_AFTER_FFC=0`
### 4.3 物体破碎 / 椒盐
- 原因: 固定窗口过窄
- 修复: 改为自适应窗口
### 4.4 蒙层 / mura 感
- 原因: 常数 ref 或错误参考导致 mura 直接显示
- 修复: 启动阶段采集每像素平场 `g_ref_mura`
### 4.5 中上固定遮蔽
- 原因: 动态 badmap / scene-derived bad map 会把错误结构钉在屏幕坐标
- 修复: `BADMAP_ENABLE 0`
### 4.6 右下炫光
- 原因: 误把某层处理当成全帧平滑
- 修复: 去掉整帧 3x3 blur 路径
### 4.7 最高温点变黑
- 原因: 调色或极值像素处理不稳
- 修复:
- 加了 `idx >= hi -> 1023` 饱和保护
- 保留保守的 `correct_zero_pixels()`
## 5. 当前稳定基线
请把下面这些当作“目前相对稳定、不要随便一起推翻”的基线:
- `CONSTANT_REF=1`
- `SBNUC_ENABLE=1`
- `BADMAP_ENABLE=0`
- 不启用显示层 `deghost_nuc()`
- 保留 `correct_zero_pixels(g_live)`
- 不使用动态 badmap
- 不重新采集 FFC 后参考
- `g_reference``g_ref_mura + shift` 生成
当前 `SBNUC` 的核心逻辑仍在 `CONSTANT_REF && g_ref_mura_ready` 分支里。
## 6. 当前主要问题
用户最新稳定反馈是:
- 画面别的问题基本都行
- 主要剩下鬼影
- 大温差目标离开后擦得还可以
- 小温差目标离开后,残影很明显,而且消失慢
也就是说问题已经不是“黑洞”了,而是“低对比度残影消退太慢”。
## 7. 已尝试过的鬼影方案与结果
以下这些路线都试过了,后续模型不要再盲目重复:
### 7.1 直接全局加速 SBNUC
试过:
- 去掉 `df` 条件
- 放宽 `dd` 阈值
- `alpha``1/64` 提到 `1/16`
结果:
- 对黑洞有帮助
- 但对小温差鬼影改善有限
- 太激进时容易把画面别的东西带坏
### 7.2 用 `dd > 120` 冻结热物体,负残差快速吸收
当前稳定版本里保留了这条思路:
- `dd > 120` 认为是热目标,冻结
- `dd < -120` 用更快步长
结果:
- 黑洞基本缓解
- 但小温差鬼影仍然慢
### 7.3 显示层去鬼影
试过:
- 最近热区 mask
- motion history
- 在显示层替换 ghost 区像素
结果:
- 效果不明显,或者直接把显示搞坏
- 用户明确反馈“显示不对了,有地方看不见了”
结论:
- 不建议再从显示层硬补丁下手
### 7.4 更复杂的热区/静止检测
试过:
- `g_sb_still`
- `g_hot_hist`
- `g_motion_hist`
- `g_disp_hot`
结果:
- 大部分复杂掩码方案收益低,且很容易引入副作用
## 8. 最后一次实际修改
最后一次修改是在当前 `SBNUC` 块上,加入了“最近热像素释放加速”:
- 新增常量:
- `SB_HOT_T 120`
- `SB_HOT_SEED 48`
- `SB_HOT_HOLD 24`
- 逻辑:
- `dd > SB_HOT_SEED` 时给该像素一个短期热记忆
- 当像素刚从热态退出,且 `dd < 0`
- 临时把更新步长提高到 `denom = 1``2`
- 目标是让“小温差尾巴”比以前更快擦掉
改动位置:
- [mag160c_demo3.c](/abs/path/C:/Project/MAG160C/csdk/tools/mag160c_demo3.c)
这一版已经成功编译并启动过,但还没有得到“明显修好鬼影”的确认。
## 9. 官方抓帧证据
已成功抓到:
- 目录: `C:\Project\MAG160C\analysis\pairs_move`
- 预览图: `C:\Project\MAG160C\analysis\pairs_move\preview`
- 统计表: `C:\Project\MAG160C\analysis\pairs_move\preview\summary.tsv`
已经确认的事实:
- 官方 `f20-ref` 的整帧残差均值并不在 `0` 附近,而是大约 `-5000`
- 官方自身也能看到“人物旧位置偏暗”的形态
- 说明鬼影不是 demo3 独有 bug,而是官方这类冻结参考体系本来就会带来的现象
几个关键数值:
- `pair_000``pair_011``d_mean` 大约在 `-5012``-5249`
- `d_std` 大约在 `735``993`
- 官方 `gray` 帧间变化大多不高,但在 FFC 附近会跳
重要提醒:
- `tsdk_pair2.c``.thr/.gain` 有重复写文件的问题
- 前面本来先按指针保存了真实表,后面又被 `dev+0x41848 / 0x41870` 覆盖
- 所以 `pairs_move/*.gain` 不能直接当纯 gain/off 表来解析
## 10. 当前源文件里还留着但未启用的东西
`mag160c_demo3.c` 里还有一些历史试验残留,很多已经不在主路径使用:
- `g_sb_prev_d`
- `g_sb_still`
- `g_hot_hist`
- `g_disp_hot`
- `g_disp_hot2`
- `g_motion_hist`
- `g_disp_prev_live`
- `g_disp_prev_ready`
- `g_disp_cur_hot`
- `g_disp_ghost`
- `g_disp_repl`
- `deghost_nuc()`
其中:
- `g_hot_hist` 现在又被重新用于“最近热像素释放加速”
- `deghost_nuc()` 仍然存在,但调用被注释掉了
## 11. 建议下一步
优先顺序建议如下:
1. 不要再做显示层补丁。
2. 继续只在 `SBNUC`/参考更新层做改动。
3. 重点针对“刚离开热目标后的低对比度负残差”做更稳的状态机。
4. 如果参数法还是没明显改善,直接上 Hardie 风格时域方差门控。
更具体地说:
### 方案 A: 做“退出热目标后的短时释放状态机”
比当前 `g_hot_hist` 更严格一点:
- 进入热目标态: `dd > hot_enter`
- 退出热目标态: 从热态回落到 `dd <= hot_exit`
- 退出后 `N` 帧内:
-`dd < 0`,快速拉 `g_ref_mura -> g_live`
- 若再次变热,立刻取消释放
这比单纯看当前 `dd` 更稳,因为它显式编码了“曾经热过,现在走了”。
### 方案 B: Hardie / Kalman 风格时域方差门控
这是最值得上的正路。
对每个像素维护:
- `d = live - ref`
- 短窗均值
- 短窗方差
规则:
- 方差小且接近背景偏移 -> 当作背景,允许吸收
- 方差大 -> 当作运动目标,冻结
这类方法比单阈值 `dd` 更适合处理“小温差但持续可见的鬼影”。
## 12. 不要轻易改的点
后续模型请避免同时改这些东西,否则很难定位:
- `BADMAP_ENABLE`
- `correct_zero_pixels()`
- FFC 重采样策略
- 调色和窗口逻辑
- 显示层 `deghost_nuc()`
- 已验证的官方 LUT / palette 路径
除非有充分证据,否则这次只改 `SBNUC` 块。
## 13. 关键文件
- 主程序: [mag160c_demo3.c](/abs/path/C:/Project/MAG160C/csdk/tools/mag160c_demo3.c)
- 官方抓帧工具: [tsdk_pair2.c](/abs/path/C:/Project/MAG160C/csdk/tools/tsdk_pair2.c)
- 当前交接: [handoff_20260811_full.md](/abs/path/C:/Project/MAG160C/analysis/handoff_20260811_full.md)
- 历史进展: [progress_reverse.md](/abs/path/C:/Project/MAG160C/analysis/progress_reverse.md)
- 官方抓帧预览表: [summary.tsv](/abs/path/C:/Project/MAG160C/analysis/pairs_move/preview/summary.tsv)
## 14. 给下一个模型的直接任务
首选任务:
- 先读本文件
- 再读 `C:\Project\MAG160C\csdk\tools\mag160c_demo3.c`
- 只改 `SBNUC`
- 目标是明显改善“小温差鬼影擦除慢”
- 不要碰别的已稳定逻辑
- 改完后编译并启动 demo 给用户实测
+138
View File
@@ -0,0 +1,138 @@
# MAG160C 逆向交接文档 v3(2026-08-12)
> **本文件是最新、最完整上下文快照。** 上一版:handoff_20260811.md。
> 本轮(2026-08-11 晚)完成**官方"读取→显示"完整管线逆向,全部实机
> 像素级验证(100.0% 精确),并交付 demo3(官方管线版)**。
## 1. 本轮成果(概览)
1. **同帧配对 harness**(tsdk_pair2.exe):同一帧抓官方 gray + RGB24 +
raw counts + palette + per-pixel 温度(m°C)+ 窗口/LUT 字段。
2. **完整显示管线逆向完毕**(Windows CoreSDKLib.dll + 实机验证):
```
USB counts → NUC: nuc[i] = clamp((counts[i]-ref[i])*3 + comp, 0, 65535)
→ 统计: mean/min/max(每帧)
→ 窗口: lo = min(max, mean-312), hi = max(min, mean+312) (X=624)
→ 灰度: idx = (nuc[i]-lo) * (0xFFC00000/(hi-lo)) >> 22 (u32 截断!)
gray = LUT1024[dev+0x4284c][idx] (非线性对比度曲线)
→ 颜色: palette[dev+0xb18][gray] (256×4 BGR,含 alpha=0)
→ 2x bilinear 放大 → 320x240 输出
```
3. **像素级验证**:raw→gray 重建 = **100.0% 精确**(与官方同帧 gray
逐像素一致,mean|diff|=0.000);palette 渲染 vs 官方 RGB24 =
**100.0% 精确**。
4. **温度公式**:temp_mC = T2E[3*raw - C],C≈5797(5784-5812 会话
漂移)。12 帧验证 mean|err| = 1.3 mC。
5. **demo3**(mag160c_demo3.c,已编译运行):完整官方管线 + 修正了
**像素偏移 bug**(旧代码 +28 是错的,正确从 data[0] 开始,探针
验证:read1=28B 头,read2=38400 像素+28B 尾)。
6. **鬼影结论**:官方 NUC 参考帧只在 FFC 时更新(冻结),无时域滤波
→ 无鬼影。demo3 沿用此机制(FFC 后重采集参考帧)。
## 2. 官方显示管线(权威,全部实机验证)
### 2.1 关键内存布局(Windows CoreSDKLib.dll 运行时对象 dev)
| 偏移 | 内容 | 说明 |
|---|---|---|
| dev+0x220 | 灰度缓冲指针 | 8-bit,显示尺寸(此处 320x240) |
| dev+0x270 | NUC 后帧指针 | u16,160x120,NUC 输出 |
| dev+0x2a0 | (另一缓冲,MapTemperature 输出目标之一) | |
| dev+0xaf0 | 显示结构 | +4 w,+8 h(此处 320x240) |
| dev+0xb18 | 调色板 | 256×4 BGRα,与渲染 100% 一致 |
| dev+0xf18 | 色彩条 | 128 级灰度渐变 RGBA |
| dev+0x42010 | 帧 min | u32(0x1800108a0 统计输出) |
| dev+0x42014 | 帧 max | u32 |
| dev+0x42018 | 帧 mean | u32 |
| dev+0x4201c | 帧 std | u32 |
| dev+0x4202c | 窗口 hi | u32(实测 ~10088-10116) |
| dev+0x42030 | 窗口 lo | u32(实测 ~9464-9492) |
| dev+0x4204c | 直方图 | 256×u32(灰度 idx>>2) |
| dev+0x4284c | **LUT1024** | 1024 字节,非线性对比度曲线 |
| dev+0x41ef0 | NUC 参考帧指针 | dev+0x41f0c=是否有参考帧 |
| dev+0x47938 | 像素数 | 19200 |
| dev+0x47948/0x4794c | comp 参数 | comp=(0xc350-offset)>>shift |
| [0x18006ea60] | NUC 增益(全局) | 会话自适应,实测=3,饱和时 -1 |
### 2.2 帧处理流程(0x180017040 = NUC+增益)
```
nuc[i] = clamp((frame[i] - ref[i]) * gain + comp, 0, 0xffff)
frame = USB 原始帧(参数),ref = dev+0x41ef0 参考帧,
gain = [0x18006ea60](饱和像素 >0x100 时减 1),
comp = (0xc350 - dev+0x47948) >> dev+0x4794c (clamp>=0)
输出写 dev+0x270 指向的缓冲
```
之后 0x180017200 做 3x3 加权滤波(系数 1/3,1/6,1/7 等,写回 dev+0x270)。
### 2.3 灰度生成(0x180010e85 = Windows MapTemperature)
```
S = 0xFFC00000 / (hi - lo) ; u32 除法
idx = (nuc[i] - lo) * S >> 22 ; u32 截断乘法!
gray[i] = LUT1024[dev+0x4284c + idx]
直方图[dev+0x4204c + (idx>>2)]++
```
### 2.4 窗口自适应(0x1800109c0/0x180010b0d)
```
统计(0x1800108a0):min→0x42010, max→0x42014, mean→0x42018, std→0x4201c
half = X/2, X 来自积分时间×增益(0x1800108a0 内部,会话恒定,实测 624)
lo = min(max, mean - 312); hi = max(min, mean + 312)
clamp lo>=0, hi<=0xffff
```
实测窗口中心 == 帧 mean(9804.8→9804 ✓),X=624 恒定。
### 2.5 温度(实测公式)
```
temp_mC = T2E[3*raw - C], C≈5797(5784-5812 逐帧漂移)
T2E:646 项 int32 表(analysis/official_t2e_table.txt,已转 C 头)
slope = (0x1000000 + span/2) / span; temp = slope*diff>>12 + (i<<12) - 0x249f0
验证:12 帧 × 19200 像素 mean|err| = 1.3 mC
```
## 3. 已导出资源(csdk/src/)
- `mag160c_official_palette256.h`:256×4 BGR(dev+0xb18,12 帧一致)
- `mag160c_official_lut1024.h`:1024 字节(dev+0x4284c,非线性曲线)
- `mag160c_official_t2e.h`:646 项(已有)
- 验证脚本:`analysis/temp/`(Python)与本次新增:
- `tsdk_pair2.c`(harness)→ `build-artifacts/tsdk_pair2.exe`
- `mag160c_demo3.c` → `build-artifacts/mag160c_demo3.exe`
- 配对数据:`analysis/pairs2/`(12 帧 × gray/rgb/raw/pal/t32),
`analysis/pairs_win/`(窗口/LUT/直方图字段),
`analysis/pairs_ffc/`(FFC 前后)
## 4. 帧解码修正(重要 bug)
**像素从第二次 bulk read 的 data[0] 开始**(探针 mag160c_layout_probe2
实测:read1 恰 28B 头,read2 = 38400 像素 + 28B 尾,尾魔数 0x1bb1b11c
@ offset 38400)。
- `mag160c_demo2.c` 的 `decode_live`(+28)是错的 → demo3 已改为 +0。
- `mag160c_frame_dump.c` 的 `fwrite(frame+28)` 也是错的(错位 14 u16),
统计均值看似正常是因为行 mura 平滑,但像素错位。
- 注意:这与官方 SDK 内部不同(官方 buf+0x1c 起拷像素,因为官方
的帧缓冲含 28B 头)。
## 5. 温度/窗口剩余问题(未完全确定)
1. **LUT 高端/低端形态**:场景只有 26-28°C,只覆盖 LUT 部分 idx。
LUT 已直接从内存取全 1024 项,无需外推 ✓(这个已经解决了)。
2. **X=624 的来源**:会话恒定,但可能随积分时间/增益模式变化。
已从 dev+0x4202c/0x42030 实测;demo3 硬编码 312 half。
3. **comp 绝对校准**:官方 comp=(0xc350-offset)>>shift 自适应;
demo3 简化为 ref 捕获时 comp=9804(官方 NUC 中心水平)。
4. **ref 捕获时机**:官方在 FFC 后捕获;demo3 沿用"FFC(1)后 30 帧
中值"方案(已验证 mura std 3560→29)。
## 6. 下一步建议
1. 实机对比 demo3 画面与官方 app(用户操作,目视确认)。
2. 若需要长期稳定性:把 comp 校准改为跟随窗口中心
(mean±312 自动居中,comp 只影响绝对温度显示,画面自适应)。
3. Linux 移植:管线纯 C 已在 csdk/src(display/t2e/palette/lut),
把 demo3 的 render 逻辑移植到 mag160c_display.c 官方版 API。
4. 热源实验(可选):宽温度范围数据可验证 LUT 外推与窗口 clamp
行为(idx 溢出回绕)。
## 7. 环境与工具
- 设备检测:`Get-PnpDevice | ? InstanceId -match 833C`
- 官方 harness:`build-artifacts\tsdk_pair2.exe <dir> <npairs> <delay_ms>`
(可选第 4-6 参数 SetTempBoundary)
- 官方 SDK:`IR_Camera_SDK-1.0.1\windows\windows\app\`(ThermalSDK/
CoreSDKLib/CameraSDK + libusb0.dll)
- 反汇编:objdump -d -Mintel;RVA 表见各 disasm_*.txt
- Python 3.10 + numpy/scipy/PIL
@@ -0,0 +1,72 @@
# MAG160C 逆向交接文档 v4(2026-08-12 深夜)
> **最新快照。** 前版:handoff_20260812.md(白天)。
> 本轮完成:官方 NUC/参考帧机制完整逆向 + demo3 修复(颗粒感/FFC 变色)。
## 1. 本轮成果
1. **完整逆向官方帧处理链(Windows CoreSDKLib.dll)**:
```
USB帧 → 帧计数分支(0x18000a0xx):
帧1-5: 0x18000c760(校正表生成, 无显示)
帧6-9: 0x18000c620(推帧0x41ee0平滑器→生成ref) + 0x18000c760
帧==9: 发送FFC(0x6bb6b672,1)
帧10+: 0x18000ca30(主处理)
→ 0x18000c950: 0x41f10平滑器(模式1: 首帧拷贝+EMA1/2) → f20缓冲(0x41f20)
→ 0x180017200(NUC查表: d=(f20-ref)>>1, 阈值表*(0x41858), 增益表*(0x41870),
out=gain×d>>12+off, shift=0x11c=12) → dev+0x270
→ 0x180017330(3x3加权, 表驱动) → dev+0x270
→ 0x18001e920(0x41f40平滑) → 0x180010780(温度) → 0x1800109c0(窗口) → 放大
```
2. **参考帧机制(决定性)**:
- ref(0x41ef0) = 平滑器0x41ee0(模式4)输出 = 启动时~22帧累加>>2(≈5.4×帧均值)
- 只在启动(帧6-9)与FFC时更新;**正常流中不更新**(帧10+不推帧0x41ee0)
- FFC后ref仅全局偏移变化(-145/-214),空间结构保持
- **官方ref含启动场景,不吸收后续场景 → 物体显示正常、无鬼影**
3. **表机制**:
- 0x41848/0x41850 = 端点表1, 0x41858 = 插值输出(阈值表, 3项s16全局)
- 0x41860/0x41868 = 端点表2, 0x41870 = 插值输出(增益表 nsegs×19200×(gain,off))
- 0x180016dd0/0x180016f10 = 分段线性插值(按dev+0x54温度在断点0x41564间插值)
- 0x180016ae0 = 端点表初始化(帧0, 从配置0x417xx加载)
- nsegs=0x41554=3, 端点索引0x41840, 断点0x41564/0x41568
4. **demo3 修复(用户反馈: 颗粒感+FFC变色)**:
- **斜率 3 → 0.41**(官方量级: 物体100 counts → nuc 41; 之前3倍放大27倍噪声)
- **FFC后不重采集ref**(REF_REINIT_AFTER_FFC=0, 只rebase全局偏移) —
重采集会吸收场景使物体消失/FFC后画面错
- 3x3双向滤波(live与ref同滤波, mura匹配)
- 像素偏移+0修正, 启动延迟采集, 采集期间冻结显示
- **实测: temporal gray std 54→22, FFC前后gray均值恒定(74.5), 无颜色跳变**
## 2. 关键实测数据(pairs_nuc2)
| 量 | 值 |
|---|---|
| nuc(0x270) | mean~9706 std~41 时域噪声13.7 |
| f20(0x41f20) | ~10855±3639(≈USB帧, 含mura) |
| ref(0x41ef0) | ~14654±3656(=f20+3875, mura同源corr0.998) |
| d2=(f20-ref)>>1 | std~114, 与nuc相关仅0.04(坐标/语义错位, 未完全解密) |
| 增益表seg1 | gain均值3352(≈0.82×4096), off≈9660(≈nuc) |
| 平滑器模式 | 0x41fd8=4(0x41ee0), 0x41fdc=1(0x41f10), 0x41fe0=1, 0x41fe4=5 |
| 帧流参数 | 2d78=4 2d88=5 2d8c=4 2d90=1800 2d94=250 |
## 3. demo3 当前状态
- 编译: build-artifacts/mag160c_demo3.exe
- 管线: counts →(3x3+坏点)→ nuc=(live-ref)×0.41+9804 → 窗口[mean±312] →
LUT1024 → palette256 → 2x显示
- ref: 启动nread>=130后45帧中值采集; FFC后只rebase全局偏移(不重采集)
- 诊断: demo3_diag.txt(每帧 ref/live/nuc/窗口统计), demo3_auto_*.bmp(自动截图)
## 4. 未完成/待验证
- [ ] 官方nuc与(f20-ref)的精确映射(相关0.04, 表机制复杂未完全解码)
- [ ] demo3斜率0.41的实机画面确认(对比度/颜色方向)
- [ ] 0x180016ae0端点表初始化(0x417xx配置/DDT加载)
- [ ] FFC状态机(0x73bf4, 帧号75/130)
- [ ] ThermalSDK.dll完整逆向
- [ ] 启动黑屏~9秒可优化(更早采集或显示预热画面)
- [ ] 热源场景验证(确认颜色方向: 热=白/红)
## 5. 环境
- 设备: Get-PnpDevice | ? InstanceId -match 833C
- harness: build-artifacts/tsdk_pair2.exe <dir> <npairs> <delay> [边界参数]
- 官方SDK: IR_Camera_SDK-1.0.1/windows/windows/app/
- 反汇编: analysis/disasm/coresdk_windows_disasm.txt(193k行, UTF-16)
- Python: numpy/scipy/PIL
+163
View File
@@ -0,0 +1,163 @@
# MAG160C 热像仪渲染管线交接文档 v5
> 项目:MAG160C 热像仪(160x120, USB, VID 0x833C)自实现渲染管线 demo3
> 目标:复刻官方 CoreSDKLib.dll 渲染效果
> 状态:颗粒 ✓ / FFC 颜色 ✓ / 蒙层 ✓ / **黑洞·黑拖影 ✗(当前阻塞)**
---
## 1. 已解决问题(历史)
| 问题 | 根因 | 修复 |
|---|---|---|
| 画面颗粒重 | NUC 斜率 3 过大(官方 0.19) | `NUC_GAIN=19`(0.19) |
| FFC 后颜色跳变 | FFC 后重采集 ref 吸收场景 | `REF_REINIT_AFTER_FFC=0`:FFC(1) 后只 rebase 全局偏移 |
| 物体显示破碎/椒盐 | 固定窗口 ±312,物体 nuc 超窗口饱和 | 自适应 P2..P98 百分位窗口(下限 624,官方实测) |
| 画面"蒙一层东西" | 常数 ref 不做平场,mura(空间 std 3560)直接显示 | 启动采集每像素平场(median,运动门控) |
| 黑拖影/黑洞 | ref 冻结 + 物体移动 → 旧位置 d = live-ref < 0 → nuc 低 → 黑 | **未解决**(见 §3) |
---
## 2. 官方逆向成果(已验证,disasm 证据)
### 2.1 渲染数据流
```
USB帧(type=0)
→ 平滑器 0x41f10(模式1: 首帧 memcpy + EMA) → f20 (dev+0x41f20)
→ NUC 查表 0x180017200: d2=(f20-ref)>>1, 段查表, clamp[0,65535]
→ 3x3 加权 0x180017330(写回) → dev+0x270 (raw/NUC输出)
→ 窗口 0x4202c/0x42030 (hi/lo, 动态) → gray (dev+0x220, 2x 升采样 320x240)
→ 调色板 256 色 (pal[0]=黑, pal[255]=白) → RGB
```
### 2.2 NUC 查表(0x180017200, 完整逆向)
```
每像素:
d2 = (f20[i] - ref[i]) >> 1 (sar)
段选择: for(edx=0; edx<nsegs-1; edx++)
if (d2 <= s16 thresh[edx]) break; (thresh 在 dev+0x41858, 值 [3136, 2484, 644])
偏移 = edx * 19200 * 2
nuc = (u16 gain[段][i] * d2) >> (shift=0x11c=12) + u16 off[段][i]
clamp [0, 65535]
→ dev+0x270
```
- gain/off 表:dev+0x41870,nsegs=dev+0x41554=3,每段 19200 像素 × (gain u16, off u16)
- **注意:d2 全负(正常场景,ref≈f20+3800)→ 段0。段1/2 用于大 d(物体),官方对物体响应待验证**
### 2.3 参考帧 ref(dev+0x41ef0)
- 来源:平滑器 0x41ee0(模式4 = 累加平均 acc+=in, out=acc>>2)
- 采集:启动帧 6-9(仅 4 次调用 0x18000c620),之后**冻结**
- FFC 后:只全局偏移变化(-145/-214 counts),空间结构不变(不重采集)
- 与 f20 同源 mura(corr 0.998),精确抵消 mura → d2 空间 std 仅 ~114
### 2.4 FFC 触发(0x18000a1d2, 0x180002f50)
- 条件 A:帧号 ≥ r9 + [0x2d90](0x2d90=1800 帧 ≈ 120s 周期)
- 条件 B:帧号 ≥ r9 + r8(30/100 冷却)且 |sensor_temp - last| > [0x2d94]=250
- 动作:发 FFC(0) (0x6bb6b672, param=0),更新温度记录(0x54/0x58),重置计数(0x49c)
- FFC(1) 在 9 帧后(0x18000c620 链路),type=1 帧期间不渲染(官方冻结画面)
### 2.5 温度
- NUC 输出与温度**完全正相关(corr=1.0, 热=亮)**
- temp_mC = T2E 查表(3×nuc - C, C~5797, 表 0x249f0 645 项)
- 平滑器 0x41f40(模式?)→ 0x180010780(温度)
### 2.6 关键结论
- **官方 ref 冻结 → 官方同样会黑洞/拖影**(移动物体),官方无场景检测
- 官方 FFC 周期 120s,比 demo3 早期 27s 长得多
- 官方窗口动态(无手 span≈624,有手 span≈1381)
---
## 3. 当前阻塞:黑洞/黑拖影
### 3.1 现象
用户敲键盘(手在镜头前小幅移动):手移动后,手旧位置/背景最热处显示为黑色(d 负 → nuc 低 → 黑),拖影明显。
### 3.2 物理机制
```
ref 冻结(平场) → 物体移动 → 旧位置:
d = live(当前背景) - ref(启动背景)
若背景温度漂移/场景变化 → d < 0 → nuc < comp → 黑
```
### 3.3 已试方案与结果
| 方案 | 结果 | 失败原因 |
|---|---|---|
| 固定窗口 ±312 | 物体破碎 | 窗口太窄 |
| FFC 后重采集 ref | 颜色跳变 | 吸收场景 |
| 冻结 median 平场 | 黑洞 | ref 冻结 |
| 常数 ref(每帧全局最低) | 无黑洞但蒙层 | 不平场 |
| 平场+min 平移 | 蒙层✓(修复采集 bug 后) 黑洞回归 | ref 仍冻结 |
| SBNUC 条件更新(df<50 且 dd<80, α=1/64) | 蒙层✓ **黑洞仍存** | 见 3.4 |
### 3.4 SBNUC 失效根因(diag 实测 d=(-467±1479))
1. **df 条件(帧差<50)**:手快速移动时,手经过的像素帧差大 → 永不满足 → 永不吸收 → 拖影持续
2. **dd 阈值 80 太严**:背景 d 空间 std ~458 → 大部分背景像素 |d-med|>80 → 吸收率极低
3. **α=1/64 太慢**:完整吸收需 64 帧(4s),拖影长
4. **背景漂移**:场景整体漂移(实测 d mean 从 +1734 → -467,场景变冷)→ d 负 → 黑
### 3.5 下一步建议(按优先级)
1. **改 SBNUC 参数**:去掉 df 条件(或放宽到 200),dd 阈值 ~120,α=1/16(1s 吸收)
- 理由:只用 dd(物体距离)判断,手经过区域(背景没变,d≈med)→ 立即吸收
2. **每像素时域 d 方差检测**(Hardie Kalman SBNUC, IEEE TIP 1998):
- 维护 d[i] 短窗方差:背景稳定(方差小)→ 吸收;物体经过(方差大)→ 不吸收
3. **双缓冲 ref**:背景 ref(条件更新)+ 物体区域冻结保护
4. 验证指标(demo3_diag.txt):d mean → 0,d 空间 std 应 < 300,物体移动后旧位置 d 快速回 0
5. 若参数方案有效,复刻官方查表(0.19 线性 → 官方分段)对比官方画面
---
## 4. 代码结构与关键参数(demo3.c)
```
常量:
CONSTANT_REF=1 常数/平场+SBNUC 模式
NUC_GAIN=19 (0.19)
FFC_PERIOD=1800 官方周期(120s)
FFC_GAP=9
REF_INIT_N=12 平场采集帧数
REF_REINIT_AFTER_FFC=0 FFC 后不重采集
WIN_HALF=312 窗口下限半宽(P2-P98 自适应)
平场采集:g_ref_phase==1(启动 nread>=60 后),运动门控(帧差>30 或 4% 像素动 → 重置窗口)
SBNUC 块:CONSTANT_REF && g_ref_mura_ready 分支(行 ~885):
med = 直方图中值(live - g_ref_mura)
if (|live-prev|<50 && |(live-mura)-med|<80) mura += (live-mura)/64
平移: ref = mura - min(mura) + min(live)
关键数组:g_ref_mura[NPIX] 平场, g_reference[NPIX] 渲染用
```
### 编译
```
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
```
### 运行/测试
```
Stop-Process -Name mag160c_demo3 -Force
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"
日志:demo3_diag.txt(每帧 ref/live/nuc/窗口/d), demo3_auto_*.bmp(每 30 帧自动截图)
用户测试:启动 → 等平场(~5s) → 手放镜头前小幅移动(敲键盘) → 观察黑拖影
```
### 工具
- `build-artifacts/tsdk_pair2.exe <dir> <npairs> <delay>`:官方 SDK 抓帧(需先停 demo3;若 "no channel" 需重插设备)
- `build-artifacts/mag160c_frame_stats.exe`:libusb 直连抓帧
- 反汇编:`analysis/disasm/coresdk_windows_disasm.txt`(UTF-16,193k 行)
- 官方数据:`analysis/pairs_nuc2/pair_000.*`(f20/ref/raw=nuc/gray/rgb/cbrgb/t32/thr/gain)
---
## 5. 方法参考(专利/开源)
- Scribner et al., "Adaptive nonuniformity correction for IR focal-plane arrays using neural networks", SPIE 1541, 1991/1993 — 最经典 SBNUC
- Hardie et al., "Scene-based nonuniformity correction with reduced ghosting using a recursive Kalman filter", IEEE Trans. Image Process. 7(10), 1998 — **专治移动物体鬼影,与当前问题最相关**
- FLIR/ULIS 自适应 FFC 专利(如 US 7,940,301 系列)— 静止场景才刷新校正表
- 开源:seek-thermal-suite(madsci1016)、Melexis MLX90640 API(无快门自校正)
- 核心思想:背景像素"静止且接近参考"才更新;物体区域冻结
## 6. 未完成逆向
- [ ] 官方查表段 1/2(大 d 物体)的增益/偏移值与物体响应(线性 0.19 vs 官方分段)
- [ ] 官方窗口(0x4202c/0x42030)更新机制(动态 span 怎么算)
- [ ] 0x180016ae0 菜单表初始化(0x417xx/DDT)
- [ ] FFC 状态机 0x73bf4(帧 75/130)
- [ ] 官方输出 2x 升采样算法(0x220 38400 → RGB)
+56
View File
@@ -0,0 +1,56 @@
# MAG160C 全量逆向进度跟踪 (2026-08-12 深夜轮)
# 心跳: 每阶段写文件, 中断可恢复
## 本轮新增结论(2026-08-12 深夜)
- [x] 平滑器 0x1800011a0 完整: 模式1=memcpy, 模式2=EMA(1/2),
模式4+=计数1-3只累加(缓冲C u32), 计数==模式输出 acc>>shift, 计数清零acc保留
- [x] 平滑器对象: dev+0x41ee0(模式=0x41fd8=4, 缓冲A/B=refp, C=acc),
dev+0x41f10(模式=0x41fdc=1直通, A/B=f20p), 0x41f40(0x41fe0=1),
0x41f80(0x41fe4=5); 结构 [+0]=vtable [+8]A [+0x10]B [+0x18]C [+0x20]cnt
[+0x24]pix [+0x28]mode [+0x2c]enable
- [x] ref(0x41ef0) = 平滑器0x41ee0输出 = acc>>2, acc=5.41帧累加(启动时推帧~22次)
ref ≈ f20+3875(常数), mura相同(corr 0.998), FFC时更新(全局-145/-214)
- [x] USB帧线程(0x18000a0xx): 帧计数(0x73bec)分支:
1-5帧: 0x18000c760(校正表生成); 6-9帧: 0x18000c620(推帧0x41ee0)+0x18000c760;
帧==9: 发FFC(0x6bb6b672,1); 10+: 0x18000ca30(主处理)
流参数: 2d78=4 2d88=5 2d8c=4 2d90=1800 2d94=250
- [x] 帧处理主链: USB线程 → 0x18000ca30(ebx==1直通, 否则0x18000c950)
→ 0x18000c950: 0x41f10平滑器 → 0x180017200(NUC查表) → 0x180017330(3x3)
→ 0x18001e920(0x41f40) → 0x180010780(温度) → 0x1800109c0(窗口) → 放大
- [x] 0x180017200 NUC查表: A=*(dev+0x41f20)=f20, B=*(dev+0x41ef0)=ref
d=(f20-ref)>>1; 阈值表=*(dev+0x41858)(3项s16); 增益表=*(dev+0x41870)
(nsegs×19200×(gain u16, off u16)); out=gain×d>>12+off(0x11c=12), clamp
nsegs=0x41554=3; 0x41840=3(端点索引); 断点0x41564/0x41568
- [x] 表生成: 0x41848/0x41850 = 端点表1, 0x41858 = 插值输出(阈值表)
0x41860/0x41868 = 端点表2, 0x41870 = 插值输出(增益表)
0x180016dd0/0x180016f10 = 分段线性插值(按dev+0x54值在断点间插值)
0x180016ae0 = 端点表初始化(帧0, 加载配置0x417xx区域)
表每帧更新(温度相关)
- [x] 0x180017330 3x3: 表驱动(0x41878指针, 0x41654项), 8邻域加权平均
case0: 8点平均>>3; case1+: 加权(1/3,1/6,1/7等)
- [x] 窗口: lo=min(max,mean-312) hi=max(min,mean+312) X=624(每帧统计后)
- [x] 灰度: idx=(nuc-lo)*0xFFC00000/(hi-lo)>>22, gray=LUT1024[dev+0x4284c]
- [x] 温度: temp_mC = T2E[3*raw - C], C~5797
## 实测关键数据(pairs_nuc2)
- nuc(0x270): mean~9706 std~41 时域噪声13.7
- f20(0x41f20): mean~10855 std~3639(≈USB帧, 含mura)
- ref(0x41ef0): mean~14654 std~3656(= f20+3875)
- d2=(f20-ref)>>1: std~114, 与nuc相关性仅0.04!!
- 官方nuc主体不是d2的线性函数; 线性最优斜率0.04-0.19(MAD 23-47)
- gain表(0x41870): seg1增益均值3352(≈0.82×4096), 偏移≈9660(≈nuc)
- off表与nuc相关性低(0.2/-0.47/0.1)
- 线性近似重建gray误差11-20级(不可用) → 官方查表强非线性必需
## demo3 问题根因(用户反馈: 颗粒感+FFC变色)
1. 颗粒感: demo3 nuc=(live-ref)*3+comp 斜率3 vs 官方~0.1-0.8 → 噪声/对比放大3-27倍
2. FFC变色: demo3 ref采集(30帧中值)时机/方式与官方(启动22帧累加+FFC更新)不同;
FFC后基线漂移期间旧ref不匹配 → nuc爆炸 → 花屏
3. 像素偏移bug已修(+0), 3x3双向滤波已加, 冻结显示已加
## 下一步(未完成)
- [ ] 精确确定官方nuc对d2的斜率/查表(需要热源场景或更多分析)
- [ ] demo3按官方斜率修改并实机验证
- [ ] 0x180016ae0端点表初始化(0x417xx配置)完整逆向
- [ ] FFC状态机(0x73bf4, 帧号75/130)
- [ ] ThermalSDK.dll完整逆向
+182
View File
@@ -0,0 +1,182 @@
# MAG160C 瀹樻柟绠$嚎瀹屾暣閫嗗悜缁撴灉锛圙hidra 鍏ㄩ噺锛?026-08-13锛?
> 鏈枃妗h褰?CoreSDKLib.dll 瀹樻柟娓叉煋绠$嚎鐨勫畬鏁村弽缂栬瘧缁撹銆侀€愬儚绱犻獙璇佺粨鏋滐紝
> 浠ュ強 demo3 鏃х増"楝煎奖"闂鐨勬牴鍥犲垎鏋愩€傛墍鏈夌粨璁哄潎鏈?Ghidra 鍙嶇紪璇戜唬鐮佸拰
> 瀹樻柟娲讳綋鎶撳抚鏁版嵁鐨勫弻閲嶈瘉鎹紝涓嶆槸鎺ㄦ祴銆?>
> 宸ュ叿閾撅細Ghidra 12.1.2锛坔eadless 鎵瑰鐞嗗弽缂栬瘧锛? IDA Free 8.4锛堜氦浜掔‘璁わ級銆?> 鍙嶇紪璇戜骇鐗╋細`analysis/ida/export/ghidra_dump/`锛坘eyfuncs / callers / exports锛夈€?
---
## 1. 瀹樻柟绠$嚎锛圙hidra 鍙嶇紪璇戠‘璁わ級
```
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锛團FC 鐘舵€佹満 + 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锛?-tap 鍙岀嚎鎬?+ 杈圭紭澶栨帹, 瑙?搂6锛? 鈫?璋冭壊鏉?dev+0xb18 (256脳4 BGR)
```
## 2. DDT 鏍″噯鏂囦欢锛堣〃鐨勬暟鎹簮锛?
- 浣嶇疆锛歚%TEMP%\Core<搴忓垪鍙?`锛堟棤鎵╁睍鍚嶏級锛岀敱 app 鐢?MAG_SaveDDT 淇濆瓨銆? MAG_StartProcessImage 鈫?0x18000f130 鍦ㄥ惎鍔ㄦ椂鍔犺浇锛涙枃浠惰矾寰勫瓨 dev+0x4134c銆?- 鏈満鏂囦欢宸插鍒讹細`build-artifacts/mag160c_official.ddt`锛?,856,416 瀛楄妭锛寁3锛夈€?
```
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 鏃跺簭锛?x180009ee0 / 0x180016ae0锛?
### 3.1 蹇棬娓╁害 = 璁惧娓╁害浠g悊
- 蹇棬 = 甯у熬瀛楋紙width=160 鏃?frame[len+0x24]锛夛紝姣忓抚杩涘叆 metadata銆?- dev+0x5c = 褰撳墠蹇棬锛宒ev+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锛圱[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 璇樊锛実ain 0/115200 璇樊銆?
### 3.4 ref 閲囬泦鏃跺簭锛堝叧閿紒锛?```
FFC(0) 鍚庡抚璁℃暟澶嶄綅:
idx 1..5 : 琛ㄩ噸寤猴紙鍒嗗潡锛? idx 6..9 : 閲囬泦 ref 鈥斺€?4 涓?type=1 鏍″噯甯х殑鍧囧€硷紙绱姞 >> 2锛? 锛坕dx 6 澶嶄綅骞虫粦鍣紝idx 9 鍙?FFC(1)锛? idx 10..13 : 琛ㄩ噸寤? idx 14+ : 姝e父娓叉煋锛坮ef 鍐荤粨锛岀洿鍒颁笅娆?FFC锛?```
- **ref = 4 涓?type=1 甯х殑鍧囧€?*锛屼笉鏄満鏅抚锛?- type=1 甯у潎鍊煎疄娴?~15000-16300锛宼ype=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锛圡AE 0.118锛墊
| NUC + 鐩插厓琛ュ伩 | **diff 0/19200锛圡AE 0.0000锛?* |
| 绐楀彛 [lo,hi] | 涓庡畼鏂瑰畬鍏ㄤ竴鑷?|
| 鐏板害 (LUT+idx) | 160 灞?19200/19200 |
| 2x 鍗囬噰鏍?| **diff 0/76800** |
| demo3 C 瀹炵幇绂荤嚎澶嶇畻瀹樻柟鏁版嵁 | NUC/绐楀彛/鐏板害鍏ㄩ儴 0 璇樊 |
LUT 濉厖鍏紡锛氬湪姝g‘ center 涓?834/1024 绮剧‘锛屽叾浣?卤1~卤58 涓?璺ㄥ抚鎹曡幏鍋忓樊锛坔1024/cdf/lut 涓嶅悓甯э級+ center 鏃跺煙骞虫粦鎵€鑷淬€?
## 5. LUT1024 閲嶅缓绠楁硶锛?x180011950 + 0x180011ee0锛?
1. 1024-bin 鐩存柟鍥撅細bin = ((nuc-lo) * 0xffc00000/(hi-lo)) >> 22锛坲32 绠楁湳锛?2. 3-tap 灏卞湴骞虫粦锛坆in 0..1022锛?3. **0x8c 鍒嗘敮锛坙ive=1锛?*锛氭壘骞虫粦鍚庣洿鏂瑰浘绱 >= 鎬昏鏁懊?/4 鐨勭涓€涓?bin锛? CDF 涓績 = max(鍧囧€糱in, 璇in)锛堝疄娴?545 鈫?631锛屽畼鏂?633锛岃法甯у樊 2锛?4. 鍙屽悜 CDF锛堜粠涓績鍚戜袱渚х疮绉級锛? - 鍚戜笅锛氭瘡绱Н u21(=total>>12) 涓鏁?+0x100
- 鍚戜笂锛?8*u21 鏃舵瘡 u21 涓?+0x100锛?=8*u21 鏃舵瘡 bin +0x200
5. 瀵规瘮搴︽洸绾?curve[k] = max(1, (0x300000 + k*0x800)>>13)
6. 涓績鍊间簩鍒嗘悳绱紙fmin/fmax 瀵瑰簲 LUT 绔害鏉燂級
7. center 鏃跺煙骞虫粦 + 閲嶅~
楠岃瘉锛氱敤瀹樻柟鍚屽抚 cdf + 涓績鎼滅储锛孡UT 123/1024 宸紓锛屽叏閮ㄩ泦涓湪 lres 涓婃柟
鐏板害杩囨浮鍖猴紙卤1-7 绾э級锛屾潵婧愭槸瀹樻柟 SDK 鎶撳抚鐨勮法甯у紓姝ワ紙cdf/lut 涓嶅悓甯э級锛?闈炵畻娉曞樊寮傘€傚畼鏂逛細璇濆疄娴嬪惎鐢ㄨ矾寰?= 0x109c0 鈫?0x11ee0 鈫?0x19740锛?temporal(0x41fe0=1)/甯у樊(0x230=0)/闅旇骞虫粦(0x41fe8=0)/0x30 gray 璺緞(0x30=0)
鍏ㄩ儴鍏抽棴锛宒emo3 榛樿涓€鑷达紝temporal 宸茬Щ妞嶄綔澶囩敤寮€鍏炽€?
## 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锛坴4锛孲BNUC 鐗堬級涓庡畼鏂圭殑宸紓锛屾瘡涓€鏉¢兘鏈変笂闈㈢殑璇佹嵁锛?
### 7.1 ref 璇箟瀹屽叏涓嶅悓锛堟渶鏍规湰锛?| | 瀹樻柟 | 鏃?demo3 |
|---|---|---|
| ref 鏉ユ簮 | FFC 绐楀彛 4 涓?**type=1 鏍″噯甯?*鍧囧€?| 鍚姩鍦烘櫙 12 甯т腑鍊?|
| ref 鍊煎煙 | 鍦烘櫙 + ~2200 counts锛坉 鎭掕礋锛墊 鈮?鍦烘櫙锛坉 鈮?0锛墊
| 鍒锋柊鏃舵満 | **姣忔 FFC 瀵逛箣鍚?* | 浠呭惎鍔ㄤ竴娆★紙FFC 鍚庡彧 rebase 鍏ㄥ眬鍋忕疆锛墊
| FFC 涔嬮棿 | 鍐荤粨 | 鍐荤粨 + SBNUC 鑷剤 |
鍚庢灉锛?- 瀹樻柟 NUC 杈撳嚭 鈮?9800锛坥ff 娈甸厤鍚?d 鎭掕礋锛夛紝鏃?demo3 杈撳嚭 鈮?10900+锛坉鈮?锛夛紝
涓よ€呭鍚屼竴鍦烘櫙鐨勭敾闈㈢瓑绾ч兘涓嶅悓锛屾洿璋堜笉涓婃畫褰辫涓轰竴鑷淬€?- 鏃?demo3 鍚姩閲囬泦涓€娆?ref 鍚庢案涓嶅埛鏂?鈫?鍦烘櫙鍩虹嚎婕傜Щ锛堝璁惧鍗囨俯銆? 鐜娓╁害鍙樺寲锛夊悗 ref 澶遍厤 鈫?鏃т綅缃嚭鐜?榛戞礊/榛戞嫋褰?锛屼笖鍙兘绛夋墜鍔?FFC銆?- 瀹樻柟姣忔 FFC 閲嶉噰 ref 鈫?娈嬪奖鏈€澶氭寔缁埌涓嬫 FFC锛堝懆鏈?120s 鎴栧揩闂ㄦ紓绉?250 瑙﹀彂锛夛紝
涓旇〃涔熸寜鏂板揩闂ㄩ噸寤猴紝鍖归厤濮嬬粓鎴愮珛銆?
### 7.2 NUC 琛ㄤ笉鏄潤鎬佺殑
- 瀹樻柟锛氳〃 = DDT 绔偣鎸夊揩闂ㄦ俯搴?Q12 鎻掑€硷紝FFC 鍚庨噸寤恒€?- 鏃?demo3锛氶潤鎬佹姄鍙栬〃锛堟煇娆′細璇濈殑鎻掑€肩粨鏋滐級锛屾俯搴︽紓绉诲悗澶遍厤锛? 琛ㄧ幇涓?灏忔俯宸畫褰辨摝闄ゆ參"鈥斺€旀湰璐ㄦ槸琛ㄤ笌褰撳墠娓╁害涓嶅尮閰嶉€犳垚鐨勬畫宸紝
涓嶆槸鐪熸鐨勬樉绀哄眰楝煎奖銆?
### 7.3 SBNUC 鏄潪瀹樻柟 hack锛屽紩鍏ユ绾ч褰?- 鏃?demo3 鐨?鏇剧儹鍙樺喎蹇€熷惛鏀?浣庢俯闈欐涓嶅惛鏀?鐘舵€佹満锛屼細鎶婁綆娓╃墿浣? 鎺掗櫎鍦?ref 涔嬪 鈫?鐗╀綋绂诲紑鍚庣暀涓嬩寒娈嬪奖锛涙妸鏇剧儹鍍忕礌鍐欏洖 ref 鈫? 鏀瑰彉灞€閮ㄥ熀绾?鈫?鏂伴粦娲炪€?- 瀹樻柟娌℃湁 SBNUC锛歳ef 鍐荤粨 + 瀹氭湡 FFC 鍒锋柊鏄畼鏂瑰敮涓€鐨?鍘婚褰?鏈哄埗銆?
### 7.4 鐩插厓琛ュ伩缂哄け
- 瀹樻柟鏈?32 鏉″浐瀹氱洸鍏冭褰曪紙閭诲煙骞冲潎锛夛紝鏃?demo3 瀹屽叏娌℃帴 鈫?姝诲儚绱? 鐩存帴鏄剧ず锛? 鍊奸粦鐐癸級锛屾浘鐢ㄥ姩鎬?badmap 灏濊瘯 鈫?鍙嶈€屾妸鍦烘櫙缁撴瀯閽夊湪
灞忓箷鍧愭爣锛圔ADMAP_ENABLE=0 鍚庢畫鐣欓粦鐐癸級銆?
### 7.5 绐楀彛/鐏板害宸紓
- 瀹樻柟绐楀彛 = [min(fmin, mean-312), max(fmax, mean+312)]锛? 鏃?demo3 鐢?P2..P98 鑷€傚簲 鈫?瀵规瘮搴﹁涓轰笉鍚岋紝浣嗕笉鏄褰变富鍥犮€?
### 7.6 缁撹
鏃?demo3 鐨?楝煎奖"鏄?ref 璇箟閿欒 + ref/琛ㄤ笉闅?FFC 鍒锋柊 + 闈欐€佽〃 +
闈炲畼鏂?SBNUC 鍏卞悓閫犳垚鐨勶紝涓庡畼鏂?鍐荤粨 ref 鐨勮交寰棫浣嶇疆鍋忔殫"涓嶆槸
鍚屼竴涓幇璞°€備慨澶?= 瀹屾暣澶嶅埢瀹樻柟绠$嚎锛坉emo3 v5 宸插疄鐜帮紝瑙佷笅锛夈€?
## 8. demo3 v5锛堝畼鏂瑰鍒伙級瀹炵幇鐘舵€?
### 8.1 瀹樻柟 0x18000ca30 璋冪敤閾惧疄鐜板鐓ц〃锛?026-08-14 瀹¤锛?
| 瀹樻柟鐜妭 | 鎺у埗瀛楁(live) | demo3 | 璇存槑 |
|---|---|---|---|
| 甯ч鏍囧織娓呴浂 0x4203c/40/44/04 | 鈥?| 鉁?| 宸茶ˉ |
| c950: f20 骞虫粦(mode=1 鐩撮€?+NUC+blind | 41fdc=1 | 鉁?| NUC+blind 0/19200 璇樊 |
| temporal 0x18001e920 | 41fe0=1(鍏? | 鉁?宸茬Щ妞?| 0x41fe0>1 鍚敤锛岄粯璁ゅ叧=瀹樻柟 |
| 甯у樊璋冩暣 0x1800175a0 | 230=0(鍏? | 猬?鏈Щ妞嶄富浣?| 瀹樻柟鍏筹紝绛変环 |
| 闅旇骞虫粦 0x1800184f0/660 | 41fe8=0(鍏? | 猬?鏈Щ妞嶄富浣?| 瀹樻柟鍏筹紝绛変环 |
| 缁熻 0x180010780 | 鈥?| 鉁?| min/max/mean/std 涓€鑷?|
| 绐楀彛+LUT+gray 0x1800109c0/0x11ee0 | 90=0, 8c=1 | 鉁?| 鍚?0x8c 75% 涓績銆乽niform 鍒嗘敮 |
| 0x30 gray 瑕嗙洊璺緞 0x180017770/0x17c40 | 30=0(鍏? | 猬?鏈Щ妞嶄富浣?| 瀹樻柟鍏筹紝绛変环 |
| 鍗囬噰鏍?0x19xxx | 2x | 鉁?2x | 4x/8x/1.5x 鏈Щ妞嶏紙褰撳墠 2x锛墊
| 娓╁害鎹㈢畻 0x18000d2a0 | 鈥?| 鉁?counts_to_c | 鎺㈤拡/鏈€楂樻俯鏄剧ず |
| FFC 鐘舵€佹満 0x180009ee0 | 鈥?| 鉁?| 鍛ㄦ湡1800/婕傜Щ250/甯?5寮哄埗 |
| ref 骞虫粦鍣?0x1800011a0(mode=4) | 41fdc=1 | 鉁?| 4脳type1 甯у潎鍊?|
| 琛ㄩ噸寤?0x18000c760/16ae0/dd0/f10 | 鈥?| 鉁?| 蹇棬椹卞姩绔偣+Q12 |
| 绗簩骞虫粦鍣?0x41f90 | 41fbc=1 | 猬?鏃犺皟鐢?| 瀹樻柟鏃犳覆鏌撹皟鐢紝绛変环 |
### 8.2 2026-08-14 瀹屾暣瀵规瘮缁撴灉锛坉emo3 vs 瀹樻柟鍚屽満鏅法浼氳瘽锛?
- NUC+鐩插厓锛?/19200 鍍忕礌璇樊锛堢绾匡紝瀹樻柟鍚屽抚鏁版嵁锛?- 绐楀彛/缁熻锛氬畬鍏ㄤ竴鑷达紙绂荤嚎锛?- gray+2x锛?/76800 鍍忕礌璇樊锛堢绾匡級
- LUT 缁撴瀯锛歞emo3 [500]=41 vs 瀹樻柟 40锛孾900]=242 vs 242锛堝舰鎬佸悓鏋勶級
- gray 鐩存柟鍥剧浉鍏虫€э細0.966锛堝疄鏈鸿法浼氳瘽锛?- 闆跺儚绱狅細涓よ竟鍧囦负 0
- 宸紓鏉ユ簮锛氫細璇濋棿 ref/蹇棬宸紓锛堝畼鏂圭墿鐞嗘満鍒讹級
## 9. demo3 v5 杩愯渚濊禆涓庝骇鐗?
- DDT 鍔犺浇锛坴3 瑙f瀽锛屽惈绔偣琛?鐩插厓璁板綍锛?- 绔偣閫夋嫨 + Q12 鎻掑€硷紙蹇棬椹卞姩锛孎FC 鍚庨噸寤猴級
- ref = FFC 绐楀彛 4 涓?type=1 甯у潎鍊硷紙u32 绱姞鍣級
- NUC + 鐩插厓琛ュ伩锛?2 鏉″浐瀹氳褰曪級
- 绐楀彛/缁熻锛堝畼鏂瑰叕寮忥級
- LUT1024 閲嶅缓锛堢洿鏂瑰浘鍧囪  + center 骞虫粦锛?- 鐏板害 + 2x 鍗囬噰鏍?+ 瀹樻柟璋冭壊鏉?- FFC锛氬懆鏈?1800 / 蹇棬婕傜Щ 250 / 13 甯ч殣钘忓懆鏈?- 瀹炴満杩愯锛歴el=2銆丗FC 鑷姩瑙﹀彂姝e父銆佺敾闈㈢粺璁℃帴杩戝畼鏂? 锛坉emo3 mean 9377 vs 瀹樻柟 9640锛?
### 閬楃暀闂
1. ~~demo3 NUC 杈撳嚭鏈?2 涓浂鍍忕礌~~ **宸蹭慨澶嶏紙2026-08-13 鏅氾級**锛? 鏍瑰洜鏄?demo3 `load_ddt` 璇诲彇甯冨眬閿欒鈥斺€攂lind 璁板綍鍖哄湪鏂囦欢鎵€鏈夌鐐瑰潡涔嬪悗锛? 浠g爜鍗村湪姣忎釜绔偣鍧楀唴璇讳簡 blind锛屽鑷?EP1+ 琛ㄦ暣浣撻敊浣?1280 瀛楄妭銆? 閿欎綅琛ㄥ湪姝诲儚绱犲垪闄勮繎 seg0 gain=35321锛堝簲涓?~4000锛夛紝d2 韪╀腑鍚?NUC clamp 鍒?0銆? 淇鍚庡叏甯?0 涓浂鍍忕礌锛?65,25) nuc=9814锛実ain=4000 姝e父鍊笺€?2. LUT 閲嶅缓鐨勮法甯т竴鑷存€э細绠楁硶宸叉寜鍙嶇紪璇戝疄鐜板苟瀹炴満杩愯锛屽悓鍦烘櫙 gray 鐩存柟鍥? 涓庡畼鏂圭浉鍏虫€?0.9197锛堣法浼氳瘽锛夛紝鍓╀綑宸紓鏉ヨ嚜浼氳瘽闂?ref/蹇棬娓╁害宸紓锛? 灞炲畼鏂圭墿鐞嗘満鍒讹紝涓嶆槸 bug銆?
## 10. 鍏抽敭鏂囦欢
- 鍙嶇紪璇戝叏閲忥細`analysis/ida/export/ghidra_dump/`
- `keyfuncs_decomp_CoreSDKLib.dll.txt`锛堟牳蹇冨嚱鏁颁吉浠g爜锛? - `callers_decomp_CoreSDKLib.dll.txt`锛堣皟鐢ㄥ叧绯?+ 涓荤绾匡級
- `exports_decomp_CoreSDKLib.dll.txt`锛堝叏閮ㄥ鍑哄嚱鏁帮級
- `exports_decomp_ThermalSDK.dll.txt`
- `all_functions_decomp_libcoresdk_arm64.so_00100000.txt`锛圓RM64 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 澶嶅埗銆佽〃/鐩插厓/蹇棬/绐楀彛鎶撳彇锛?
+85
View File
@@ -0,0 +1,85 @@
# MAG160C 会话状态与恢复点(2026-08-13
> 本文件是"心跳"锚点:每个里程碑更新一次。若会话中断,新会话先读本文件 +
> `analysis/reverse_20260813_full.md`,然后运行 `tools/resume_rev.ps1` 继续。
## 当前阶段
**阶段 7/7LiThermal 对接评估 + 抽象层完成(2026-08-19**
## 已完成的里程碑
- [x] 工具链:Ghidra 12.1.2headless+ IDA Free 8.4 部署,5 个二进制全量导出
- [x] 官方管线完整反编译(DDT 加载 / 端点选择 / Q12 插值 / ref=4×type1 帧均值 /
NUC / 盲元 / 窗口 / LUT1024 / 2x 升采样 / FFC 状态机)
- [x] DDT 文件解析(%TEMP%\Core160043865 → build-artifacts/mag160c_official.ddt
- [x] 逐像素验证:NUC+盲元 0/19200、插值 0 误差、2x 0/76800、窗口一致
- [x] 鬼影根因文档:analysis/reverse_20260813_full.md §7
- [x] demo3 v5 官方复刻实现并实机运行
- [x] 零像素根因定位与修复(load_ddt 表错位)
- [x] **颗粒感修复**:demo3 显示层改为官方同款 320×240(2x 平滑灰度 +
调色板 + HALFTONE 放大),同场景 RGB 直方图相关性 0.9796
- [x] **FFC 频率对齐**:补官方"启动强制 FFC"逻辑(mode 0 帧 75 无条件
FFC(0)trace 实测启动阶段 FFC(0) 间隔 59~394 帧 ≈ 4~26s
稳定后 1800 帧 ≈ 120sdemo3 已一致)
- [x] 官方附加环节全部移植(df6c6dd):0x230 帧差 / 0x41fe8 隔行平滑 /
0x30 gray 覆盖(auto/manual/ Nx 升采样,编译开关 OFF_IMPL_*
- [x] **平台无关渲染模块 mag160c_render**5c22bfe):官方管线封装为无
平台依赖 API(输入 USB 帧 → 输出 320×240 RGB24),FFC 命令走回调
- [x] **Linux 参考程序 mag160c_linux_demo**5c22bfe):libusb + render API
+ 显示后端抽象(fbdev/DRM/LVGL),Windows 可编译验证,实测正常
- [x] **demo3 迁移到 render 库**9db940a):删内联管线 ~700 行,附加环节
经 post_nuc/post_gray 钩子挂接;FFC 时序/画面与 v5 一致,无回归
- [x] 移植评估文档 docs/linux_port_plan.md:芯片推荐 T113-S3QFP128 非
BGA ~20 元)、动画库 LVGL+rlottieLiThermal 同款)、整机成本估算
- [x] 仓库整理 + csdk/README.md 完整文档 + 本地 commit 0bfb926…9db940a7 个)
- [ ] push(等服务器上线,用户手动)
## 颗粒感问题(已解决,2026-08-14)
- 现象:官方画面比 demo3 清晰,demo3 颗粒感重。
- 根因:demo3 v5 的显示层渲染的是 160×120 原始灰度(最近邻放大到 640×480),
而官方输出是 2x 升采样后的 320×240(2-tap 平滑本身抑噪),官方 app 显示
的也是这个 2x 缓冲。
- 排查过程(已排除的嫌疑):FUN_1800175a0(0x230)是帧差偏移调整非平滑;
0x41fe8 后处理(FUN_1800184f0/660 隔行平滑)官方会话 =0 未启用;
temporal filter 0x41fe0=1 未启用;f20 mode=1 直通。
- 修复:render_bmp 改用 g_gray320(官方 2x 缓冲)上色,BMP/窗口改 320×240
StretchBlt 加 HALFTONE。
- 验证:320×240 BMP 与官方同场景 pair_000.rgb 直方图相关性 0.9796
颜色数 227 = 官方 227(之前 160×120 版只有 ~161 色)。
## 常用命令
```powershell
# 编译 demo3 v6render 库版)
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_render.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
# Linux 参考程序(Windows 可编译运行,出 BMP
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_linux_demo.exe" "C:\Project\MAG160C\csdk\tools\mag160c_linux_demo.c" "C:\Project\MAG160C\csdk\src\mag160c_render.c" "C:\Project\MAG160C\csdk\src\mag160c_ir.c" "C:\Project\MAG160C\csdk\src\mag160c_frame.c" "C:\Project\MAG160C\csdk\src\mag160c_temp.c" "C:\Project\MAG160C\csdk\src\mag160c_tcm.c" "C:\Project\MAG160C\csdk\src\mag160c_error.c" "C:\Project\MAG160C\csdk\src\mag160c_display.c" "C:\Project\MAG160C\csdk\third_party\libusb\win64\libusb-1.0.x64.a"
# 运行/停止
Start-Process "C:\Project\MAG160C\build-artifacts\mag160c_demo3.exe" -WorkingDirectory "C:\Project\MAG160C\build-artifacts"
Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
# 官方采集(先停 demo3
& "C:\Project\MAG160C\build-artifacts\tsdk_pair3.exe" "C:\Project\MAG160C\analysis\pairs_final_20260813" 8 6000
# 注意:tsdk_pair3 的 config.txt / pair_*.meta / pair_*.win 写在进程 CWD
# 跑完要把 C:\Project\MAG160C 下的 config.txt 和 pair_0*.meta/win 移回目录。
# 对比/复算脚本(Python,已归档到 analysis/reverse_tools/
# analysis/reverse_tools/compare_live.py demo3捕获 vs 官方同场景
# analysis/reverse_tools/verify_nuc.py 官方抓帧重建验证
# analysis/reverse_tools/verify_full.py 全链路验证
# analysis/reverse_tools/parse_ddt.py DDT 文件解析
# analysis/reverse_tools/parse_pcap.py pcap 抓包解析
# C:\Users\ZXC\AppData\Local\Temp\opencode\verify_c_pipeline.exe C实现离线复算
```
## 心跳约定
- 每完成一个里程碑,更新本文件"已完成的里程碑"勾选。
- 会话中断恢复流程:
1. 读本文件 + `analysis/reverse_20260813_full.md`
2. 运行 `tools/resume_rev.ps1`(自动检查设备/进程/编译/采集)
3. 按"零像素问题现场"继续
@@ -0,0 +1,890 @@
# 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` 的主链路已经被活体抓帧和反汇编共同确认:
```text
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_*.win``pair_*.win2``pair_*.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.txt`demo3 每帧诊断日志。
- `demo3_ffc.txt`FFC 调度日志。
- `demo3_rebase.txt`FFC 后参考 rebase 日志。
- `demo3_auto_*.bmp`demo3 每 30 帧自动抓图。
- `official_*.bin``official_*.rgb``official_meta.txt`:官方活体输出样本。
- `csdk_*``mag160c_*``usb_*``tsdk_*`:历史测试和探针程序。
不要把 `build-artifacts` 当成唯一源代码目录。多数文件是从不同历史版本生成的,必须结合时间、文件名和日志判断是否属于当前版本。
## 4. 硬件和 USB 环境
### 4.1 设备身份
```text
VID = 0x833C
PID = 0x0001(常见值,具体以设备描述符为准)
分辨率 = 160×120
帧像素数 = 19200
常见帧率 = 15 fps
```
### 4.2 Windows 直连端点
| 端点 | 方向 | 用途 |
|---|---|---|
| `0x03` | 主机到设备 | 命令写入。 |
| `0x82` | 设备到主机 | 命令响应。 |
| `0x81` | 设备到主机 | 实时帧。 |
| `0x84` | 设备到主机 | 大块数据或 DDT 相关读取,当前 demo3 不依赖。 |
### 4.3 原始帧布局
EP `0x81` 数据以固定标记开始:
```text
+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 命令序列
当前设备实测的初始化序列:
```text
0x6BB6B66B4 字节
0x6BB6B66C4 字节
0x6BB6B66F4 字节
0x6BB6B672,参数 0,8 字节,通常连续发送两次
等待约 300 ms
0x6BB6B6734 字节,启动
```
停止:
```text
clear_halt(0x03)
clear_halt(0x82)
0x6BB6B6744 字节
```
FFC
```text
0x6BB6B672 + uint32 参数
```
不要把普通 4 字节命令误写成 8 字节;当前设备对普通命令的 8 字节形式可能 STALL。只有带参数的 FFC 使用 8 字节。
## 5. 官方 SDK 和反汇编成果
### 5.1 SDK 文件
原厂 SDK 主要在:
```text
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 启动阶段使用。
对象:
```text
dev+0x41ee0ref 平滑器对象
dev+0x41ef0ref 实际 uint16 缓冲指针
dev+0x41f10f20 平滑器对象
dev+0x41f20f20 实际 uint16 缓冲指针
dev+0x41fd8ref 相关 mode,实测为 4
dev+0x41fdcf20 相关 mode,实测为 1
dev+0x41fe0:后续可选处理模式,实测为 1
```
### 5.3 官方参考时序
启动帧计数和参数实测:
```text
0x2d78 = 4
0x2d88 = 5
0x2d8c = 4
0x2d90 = 1800
0x2d94 = 250
```
启动早期的 `c620 -> 0x41ee0` 推帧会形成 ref。正常处理帧不会每帧重新采集整幅 ref。FFC 会切换 type=1 校准帧,官方应用在这段时间冻结显示。
### 5.4 官方 NUC 查表
`0x180017200` 的关键反汇编已经确认:
```c
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);
```
实测:
```text
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 离线复算:
```text
官方 raw 与表公式重建的平均绝对误差约 0.118 counts
绝大多数像素完全一致
少量最大误差约 900 counts,来自后续盲元补偿尚未复刻
```
这是当前最可靠的官方 NUC 复刻证据。
### 5.5 NUC 表生成
相关函数:
```text
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 表:
```text
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()` 从程序工作目录加载:
```text
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_hist``g_sb_prev_d``g_sb_innov_var` 等数组和注释中保留了多轮失败方案,后继者应在确认新方案后清理,而不是继续叠加状态。
### 6.6 设置参考按钮
当前按钮文字是 `Recenter bias`,对应 `WM_COMMAND``1004`
- 如果官方表可用,只重新计算全局 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` 阈值。
- `alpha``1/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.`0x180016ae0``0x180016dd0``0x180016f10` 的 Q12 整数公式,在 demo3 中按温度插值生成 threshold/gain/off。
4. FFC 后重新生成表,但不重新把场景采入空间参考。
5. 用多温度、多次 FFC 的官方 `b03/b06``raw` 做逐像素回归。
### 9.3 优先任务 B:复刻官方盲元补偿
需要从官方活体捕获:
- `dev+0x41878` 固定记录表。
- `dev+0x41654` 对应的记录数量。
- 每条记录的目标像素、类型和邻域索引。
然后把 `0x180017330` 的原地邻域补偿接到官方 NUC 表输出后。不要使用由场景动态生成的 badmap。
### 9.4 优先任务 C:拆分参考角色
推荐最终数据结构:
```text
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 前后同一目标序列。
对每个像素同时画:
```text
live
ref
live-ref
d2
official raw
demo3 raw
gray
```
不要只看 BMP。BMP 只能说明最终表现,不能定位残影是在 USB、ref、NUC、盲元补偿还是窗口阶段产生。
## 10. 环境安装和部署
### 10.1 当前开发环境
当前主要环境是 Windows PowerShell
```text
工作目录: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,要求 `gcc``g++``ar``objdump``readelf``strings``dlltool``gendef` 可用。
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 一定有这些包。需要时可以先检查:
```powershell
python --version
python -c "import capstone, elftools; print('逆向 Python 包可用')"
```
### 10.4 SDK 和 DLL
原厂文件已经随工程提供,通常不需要再次下载:
```text
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 中执行:
```powershell
```
注意:命令只允许出现一次 `mag160c_error.c`。重复传入会出现 `multiple definition` 链接错误。
### 11.2 启动 demo3
```powershell
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 在同一个工作目录中:
```text
build-artifacts\mag160c_official_nuc_gain.bin
build-artifacts\mag160c_official_nuc_thr.bin
```
如果缺失,程序会退回旧的线性 NUC 路径,画面和残影表现不能与当前版本比较。
### 11.3 停止 demo3
```powershell
Stop-Process -Name mag160c_demo3 -Force -ErrorAction SilentlyContinue
```
必须先停止 demo3 才能运行官方 ThermalSDK 抓帧工具。
### 11.4 编译修复后的官方抓帧工具
`tsdk_pair2.c` 原来先保存真实指针表,后面又覆盖同名 `.gain/.thr`。当前修复版把后面的内联字段改存为 `.gain_inline/.thr_inline`
```powershell
```
### 11.5 官方抓帧
先确认输出目录父目录存在,再执行:
```powershell
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
```
参数含义:
```text
参数 1:输出目录
参数 2:抓取 pair 数
参数 3:官方 SDK 初始化等待毫秒数
```
`tsdk_pair2.c` 当前会在约三分之一和三分之二位置触发 FFC,适合验证 FFC 表变化,不适合完全无干预的纯运动实验。要做低温残影实验,建议另写不自动触发 FFC 的 harness。
### 11.6 日志和图像
demo3 工作目录下:
```text
```
日志文件由程序重新启动时以写模式打开,重启会覆盖旧日志。要保留一轮实验,先复制到 `analysis/` 并改名。
## 12. C SDK 构建和验证
如果已经安装 CMake
```powershell
cmake -B build -S csdk
cmake --build build
ctest --test-dir build
```
当前机器没有 CMake 时,按照 `csdk/WINDOWS_TESTING.md` 使用 MinGW 直接编译。无硬件时可以验证:
```powershell
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 编码结果示例:
```text
7e 00 07 85 02 77 00 01 00 05 7f
```
## 13. Git 和仓库维护
本工程原先只有空的 `.git` 目录,不是有效 Git 仓库。本次移交应初始化真实 Git 仓库并推送到:
```text
https://git.zxcli.top/zxc/MAG160C.git
```
仓库约 0.824 GB,包含大量 DLL、EXE、APK、PDF、PNG、BMP、抓帧和反汇编二进制。已准备 Git LFS 规则。提交前必须检查:
```powershell
```
不要把账号密码、访问令牌、私钥写入文档或代码。原厂 SDK 和 APK 可能受第三方许可限制,仓库接手者应确认仓库访问权限和内部使用范围。
## 14. 当前接手者的建议工作顺序
### 第一步:确认环境和版本
1. 阅读本文、`analysis/handoff_20260813.md``analysis/handoff_20260811_full.md`
2. 检查 `git status` 和远端分支,确认没有并行改动。
3. 检查设备 PID、驱动、DLL、表文件。
4. 编译并启动当前 demo3,保存一轮不移动时的日志。
### 第二步:验证当前路径是否真的加载官方表
1. 暂时把 `mag160c_official_nuc_gain.bin` 改名,启动一次并记录 fallback 画面。
2. 恢复表文件,再启动并记录官方表画面。
3. 比较 `demo3_diag.txt``g_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 先理解工程,再动手,不要从头重做,也不要反复尝试已经失败的显示层补丁。
```text
你现在接手 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. 用户在实机上确认低温和小温差场景都达到可接受效果。