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
+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