Files
MAG160C/csdk/README.md
T

329 lines
16 KiB
Markdown
Raw 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 C SDK (csdk/)
MAG160C 热像仪(160×120,USB VID 0x833C)的纯 C 重新实现,基于对原厂
Windows/Linux/Android SDK 的完整逆向(见 `../analysis/reverse_20260813_full.md`)。
包含:USB 会话层、帧解析/流组装、温度换算、TCM 板通信、以及**官方渲染管线的
逐像素复刻**(demo3 v5)。
## 目录结构
```
csdk/
include/mag160c/
mag160c.h 公共 C ABI(错误域、协议常量、帧格式、温度、TCM)
mag160c_display.h FFC 调度器 + 显示辅助(ref rebase 等)
src/
mag160c_internal.h 内部共享(last-error)
mag160c_error.c TLS last-error + 错误名
mag160c_frame.c 0x1bb1b11b 帧解析 + 流组装器
mag160c_temp.c Calibration / ConvertResponse2Temperature / T2E
mag160c_tcm.c FTDICommand 0x7e 组帧 + 旋转/指示灯构建
mag160c_ir.c libusb 会话(link/start/stop/ffc/info/回调/FFC调度)
mag160c_display.c 显示辅助(FFC 调度器、ref rebase、坏点)
mag160c_tables.h T2E/E2TAccQ10 温度表(从二进制提取)
mag160c_official_*.h 官方调色板 / LUT1024 / T2E(从官方活体抓取导出)
tools/
mag160c_demo3.c 主程序:官方管线复刻(DDT+NUC+盲元+窗口+LUT+2x)
tsdk_pair2.c 官方 ThermalSDK 同帧抓帧工具(含表/盲元/快门)
tsdk_pair3.c 增强采集(DDT 复制、完整端点表、盲元、h1024/cdf)
mag160c_cli.c CLI:ir-info/ffc/start/stop/tcm-rotate/tcm-light 等
mag160c_csdk_stream_test.c csdk 流测试
legacy/ 历史探索工具(归档,勿用)
tests/ test_frame / test_temp / test_tcm / test_api / test_display
CMakeLists.txt CMake 构建(可选;当前环境直接用 gcc)
WINDOWS_TESTING.md Windows 硬件测试指南(USBPcap/Wireshark/MinGW)
```
## 构建
### 方式 A:直接 GCC(当前 Windows 环境)
```powershell
# CLI 工具
gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64" -I"csdk\include" -I"csdk\src" `
-o build-artifacts\mag160c_cli.exe csdk\tools\mag160c_cli.c `
csdk\src\mag160c_ir.c csdk\src\mag160c_frame.c csdk\src\mag160c_temp.c `
csdk\src\mag160c_tcm.c csdk\src\mag160c_error.c csdk\src\mag160c_display.c `
csdk\third_party\libusb\win64\libusb-1.0.x64.a
# 主程序 demo3(官方管线复刻)
gcc -O2 -w -DMAG160C_STATIC -I"csdk\third_party\libusb\win64" -I"csdk\include" -I"csdk\src" `
-o build-artifacts\mag160c_demo3.exe csdk\tools\mag160c_demo3.c `
csdk\src\mag160c_display.c csdk\src\mag160c_error.c `
csdk\third_party\libusb\win64\libusb-1.0.x64.a -lgdi32 -luser32
# 官方抓帧工具
gcc -O2 -w -o build-artifacts\tsdk_pair3.exe csdk\tools\tsdk_pair3.c
```
libusb 依赖:`csdk/third_party/libusb/win64/`(头文件 + DLL + MinGW 导入库)。
### 方式 B:CMake(有 CMake 时)
```powershell
cmake -B build -S csdk
cmake --build build
ctest --test-dir build
```
无 libusb-1.0 时,IR 会话 API 返回 `MAG160C_ERR_NOT_SUPPORTED`;纯计算
(帧解析、温度、TCM)和对应测试无依赖可跑。
## 快速开始
### 1. 连接并启动(核心 API 流程)
```c
#include <mag160c/mag160c.h>
#include <mag160c/mag160c_display.h>
static void on_frame(uint32_t idx, const uint8_t *data, size_t len, void *user) {
/* data: 完整帧(0x1c 头 + 像素)。用 mag160c_frame_parse 解析。 */
mag160c_frame_header_t hdr;
const uint16_t *pixels;
if (mag160c_frame_parse(data, len, &hdr, &pixels) == MAG160C_OK && hdr.frame_type == 0)
; /* pixels[19200] 小端 uint16,160×120 */
}
int main(void) {
mag160c_ctx_t *ctx = NULL;
mag160c_ir_t *ir = NULL;
mag160c_init(&ctx);
mag160c_ir_open(ctx, &ir); /* 自动找 VID 0x833c,config 2,interface 0 */
mag160c_ir_prepare(ir); /* 66b/66c/66f + FFC(0)x2 + 300ms */
mag160c_ir_set_frame_callback(ir, on_frame, NULL);
/* 官方 FFC 节律(周期 1800 帧 / 间隔 9),保持 type=0 流稳定 */
static mag160c_ffc_scheduler_t sched;
mag160c_ffc_scheduler_init(&sched, 1800, 9);
mag160c_ir_set_ffc_scheduler(ir, &sched, 0);
mag160c_ir_start(ir); /* 线程收帧 + 0x6bb6b673 */
/* ... 收帧 / 读温度 ... */
int32_t temp_mc = 0;
mag160c_ir_read_temperature(ir, 80, 60, &temp_mc);
mag160c_ir_stop(ir);
mag160c_ir_close(ir);
mag160c_shutdown(ctx);
return 0;
}
```
### 2. 关键 API
| API | 说明 |
|---|---|
| `mag160c_ir_open/close` | USB 设备会话 |
| `mag160c_ir_prepare` | 官方初始化命令序列 |
| `mag160c_ir_start/stop` | 起停流(0x6bb6b673 / 0x6bb6b674)|
| `mag160c_ir_trigger_ffc(param)` | 手动 FFC(0 或 1)|
| `mag160c_ir_set_ffc_scheduler` | 挂官方节律 FFC 调度器 |
| `mag160c_ir_set_frame_callback` | 每帧回调(解析后数据)|
| `mag160c_ir_read_temperature(x,y,&t)` | 探针温度(毫摄氏度×100)|
| `mag160c_frame_parse` | 纯解析:marker/计数/类型/像素指针 |
| `mag160c_frame_stream_*` | 流组装器(乱序/分包恢复)|
| `mag160c_temp_calibrate` | 官方逐像素分段线性校准(NUC 温度域)|
| `mag160c_temp_convert_response` | 自动增益线性换算 |
| `mag160c_temp_t2e_interp` | T2E 曲线 Q13 插值 |
| `mag160c_tcm_*` | TCM 板 FTDICommand 0x7e 帧(旋转/指示灯)|
错误码域 `mag160c_error_t` 与官方 errno 域 `0xE4AE0001..` 对齐;
`mag160c_last_error()` 取线程局部最近错误字符串。
### 3. CLI 用法(mag160c_cli.exe,无硬件可跑纯计算)
```powershell
build-artifacts\mag160c_cli.exe ir-info # 枚举/信息
build-artifacts\mag160c_cli.exe frame-test # 帧解析自测
build-artifacts\mag160c_cli.exe temp-test # 温度换算自测
build-artifacts\mag160c_cli.exe tcm-rotate 5 # TCM 旋转指令编码
build-artifacts\mag160c_cli.exe tcm-light green blink # TCM 指示灯指令编码
```
### 4. 主程序 demo3(官方渲染管线复刻)
`tools/mag160c_demo3.c` 是当前维护的 Windows 直连演示/研究程序,逐像素复刻
CoreSDKLib.dll 的完整链路:
```
USB帧(type=0) → 快门提取(帧尾字) → FFC状态机(0x9ee0)
→ 端点选择+Q12插值(0x16ae0/0x16dd0/0x16f10, 快门驱动)
→ ref = FFC窗口4个type=1帧均值(0x41ee0 mode=4)
→ NUC查表(0x17200) → 盲元补偿(0x17330, 32条固定记录)
→ [可选后处理: temporal 0x1e920 / 帧差 0x175a0 / 隔行平滑 0x184f0/0x18660]
→ 统计+窗口(0x10780/0x109c0, half=312) → LUT1024重建(0x11ee0, 含0x8c)
→ 灰度+2x升采样(0x19740) → 官方调色板
```
#### 官方附加环节编译开关(默认 0 = 官方当前会话行为)
官方 SDK 中这些路径编译在内但当前会话控制字段为 0(未执行)。demo3
全部移植,用编译开关启用,运行时用按键切换测试:
| 开关 | 对应官方函数 | 官方字段 | 按键 |
|---|---|---|---|
| `OFF_IMPL_230` | 帧差调整 0x175a0 | 0x230=0 | 8 |
| `OFF_IMPL_41FE8` | 隔行平滑 0x184f0(2x)/0x18660(4x) | 0x41fe8=0 | 0 |
| `OFF_IMPL_30` | gray 覆盖 0x17770(auto)/0x17c40(manual) | 0x30=0, 0x84 | 9 |
| `OFF_IMPL_UPSCALE` | Nx 升采样 4x/8x | 当前 2x | — |
```powershell
# 默认版本(与官方行为一致)
gcc ... (见上文 demo3 编译命令)
# 全部附加环节开启版(默认展示各路径,按键切回官方默认)
gcc -O2 -w -DOFF_IMPL_41FE8=1 -DOFF_IMPL_30=1 -DOFF_IMPL_230=1 -DMAG160C_STATIC ...
```
说明:
- 0x17770(auto gray 双边滤波)的空间权重表在官方二进制 .data 节被裁剪
无法提取,demo3 用标准高斯核近似(σ=2.0),值域核按官方生成式(sqrt 核)。
- 0x17c40(manual)已逐行翻译:8x8 窗口 4x4 采样对比度增强累积 → gray。
- 0x18660 为逐行翻译(4 相位加权插值);0x184f0 为 2-tap 半分辨率插值。
- 验证:默认版无回归;41fe8=1 开启后 NUC 输出 std 从 ~250 降到 ~95(平滑生效)。
运行(需要 `build-artifacts/` 下的 DDT 表文件):
```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"
```
依赖文件(放 exe 同目录):
| 文件 | 来源 |
|---|---|
| `mag160c_official.ddt` | 官方 DDT 校准文件(`%TEMP%\Core<序列号>` 副本)|
| `mag160c_official_nuc_gain.bin` | 官方 NUC gain/off 表(历史抓取,备用)|
| `mag160c_official_nuc_thr.bin` | 官方 NUC 阈值表(历史抓取,备用)|
| `libusb-1.0.dll` | libusb 运行库 |
验证状态(2026-08-14):
- NUC+盲元 与官方同帧抓帧逐像素 0/19200 误差
- 阈值/gain 插值 0 误差;2x 升采样 0/76800 误差;窗口公式与官方一致
- 同静态场景 320×240 RGB 直方图与官方相关性 0.98(跨会话)
- 显示层 = 官方同款 320×240(2x 平滑灰度 + 调色板 + HALFTONE 放大)
- 输出日志:`demo3_diag.txt`(每帧统计)、`demo3_ffc.txt`(FFC 事件)、
`demo3_auto_*.bmp`(每 30 帧快照,320×240)
### 5. Linux 移植(mag160c_render API)
`include/mag160c/mag160c_render.h` + `src/mag160c_render.c` 是平台无关的
**官方渲染管线模块**:喂原始 USB 帧 → 输出 320x240 RGB24。内部完整实现
官方链路(快门提取→FFC 状态机→ref 采集→表重建→NUC→盲元→窗口→LUT→
gray→2x→调色板),FFC 命令通过回调发给调用方(调用方持有 USB 传输)。
```c
mag160c_render_cfg_t cfg = { .width = 160, .height = 120,
.ddt_path = "mag160c_official.ddt", .ffc_period = 1800,
.ffc_drift = 250, .startup_force_ffc = 1, .cdf_pivot_75 = 1, /* PC 参考实现自身取值 */
.ffc_cb = my_ffc_sender, .ffc_user = NULL };
mag160c_render_init(&r, &cfg);
/* 每帧: */
mag160c_render_frame(r, full_frame_with_header, 1, rgb320x240);
```
`tools/mag160c_linux_demo.c` 是 Linux 风格参考程序(libusb + render API +
display 后端抽象,Windows 上也可编译运行,输出 thermal_*.bmp 验证)。
Linux 构建:
```bash
gcc -O2 -Wall csdk/tools/mag160c_linux_demo.c csdk/src/mag160c_render.c \
csdk/src/mag160c_ir.c csdk/src/mag160c_frame.c csdk/src/mag160c_temp.c \
csdk/src/mag160c_tcm.c csdk/src/mag160c_error.c csdk/src/mag160c_display.c \
-lusb-1.0 -lpthread -lm -o mag160c_linux_demo
```
显示后端(display_rgb 中)可按目标平台实现:fbdev / DRM-KMS / LVGL image。
UI 与动画建议:LVGL(界面/交互动画)+ rlottie(Lottie 特效),参考
LiThermal 项目;主控建议全志 T113-S3(QFP128,非 BGA,~20 元,G2D 2D
加速,Tina Linux)。完整评估见 `../docs/linux_port_plan.md`。
### 6. 官方抓帧工具(tsdk_pair2 / tsdk_pair3)
用途:对照验证 demo3 与官方管线。先停 demo3,再运行(同一设备独占):
```powershell
# tsdk_pair2:官方 ThermalSDK 同帧抓帧(gray/rgb/raw/ref/f20/tables/win)
& build-artifacts\tsdk_pair2_fixed.exe C:\Project\MAG160C\analysis\pairs_xxx 12 6000
# tsdk_pair3:增强采集(+ 完整端点表/盲元记录/shutter/h1024/cdf/DDT 复制)
& build-artifacts\tsdk_pair3.exe C:\Project\MAG160C\analysis\pairs_xxx 8 6000
# 注意:config.txt 和 pair_*.meta/win 写在进程 CWD,跑完移回输出目录
```
参数:`<输出目录> <抓帧对数> <官方SDK初始化等待ms>`。
若报 "no channel":停止所有程序后重新插拔 USB。
## 测试
```powershell
# 无硬件验证(纯计算)
build-artifacts\csdk_test_frame.exe
build-artifacts\csdk_test_temp.exe
build-artifacts\csdk_test_tcm.exe
build-artifacts\csdk_test_api.exe
build-artifacts\csdk_test_display.exe
```
## 协议要点(详见 analysis/protocol_spec.md)
- 端点:EP0x03 命令 OUT / 0x82 响应 IN / 0x81 帧流 IN / 0x84 大块读 IN
- 命令 4 字节 `{magic}`,仅 FFC(0x6bb6b672) 用 8 字节 `{magic, param}`
- 帧:`0x1bb1b11b | counter | len | type(0/1) | ... | 像素 u16 LE | 0x1bb1b11c`
- 快门(设备温度代理)= 帧尾字 `frame[len+0x24]`(width=160 时)
- 温度:`temp_mC = T2E[3*nuc - C]`,C≈5797(会话相关)
## 文档索引
| 文档 | 内容 |
|---|---|
| `../analysis/protocol_spec.md` | USB 协议/帧/命令/温度完整规格 |
| `../analysis/reverse_20260813_full.md` | 官方管线 Ghidra 全量逆向 + 鬼影根因 |
| `../analysis/session_state.md` | 当前会话状态/恢复点(心跳)|
| `../交接_完整逆向工程说明_20260811.md` | 项目总交接(目录/环境/历史)|
| `WINDOWS_TESTING.md` | Windows 抓包与硬件测试指南 |
| `legacy/` | 历史工具源码归档(早期探索,功能已被现工具取代)|
## 与官方手机 App 的对齐(2026-10-06,从安卓侧验证后同步)
1. **灰度 LUT:手机对齐移植在 Android 管线,不在本 C 参考里**(2026-10-06 复核修正)。
第 29 轮曾把 `cdf_pivot_75` 改成 0 去凑官方手机 App,那个判断已作废:真机
libcxsdk 反汇编显示官方**开着** pivot,而且它的 `HistDeRedundance` 与本 C 参考
用的(PC 版)**结构不同**——固定基值 `16*(448+447*b/200)`、步进上限
`max(1,(0x1800+4j)·k>>13)`(`k=min(0x200,16·span/range)`)、反向 CDF 从
`hist[pivot]` 起、pivot=`max(center_idx,(meanBelow+meanAbove)/2)`。
**C 侧已实现**:`mag160c_render.h` 的配置项 `android_lut`(1 = 用 Android 版
`HistDeRedundance`,即 `mag160c_render.c: android_lut_rebuild()`,是 Kotlin 侧
`RenderPipeline.androidLutOf` 的逐行转写,两边同一套 golden 向量;0 = 保持 PC 语义,
与 `analysis/render_offline.c` 的黄金夹具一致),两个 demo 默认打开(真机验证:
同场景照片逐像素平均差 4.5/255、逐计数中位差 2 级,见 §10/§11)。
⚠️ **本机没有 C 编译器**(gcc/clang/NDK 都没有),所以这段 C **未编译**——
算法本身由 Kotlin golden 测试锁定,合入前请在带编译器的环境编译 + 跑回归。
- 同一节还有个 `upscale_nearest`:官方**安卓**路径没有 160→320 的插值级
(SDK 给 App 的位图就是 160×120,App 用 `new Paint(1)`(无 FILTER_BITMAP)最近邻放大),
而 PC 版的双线性会展出一层"全幅/半幅"交错细纹(暗/低温区最明显)。
真机实测:冷区细尺度列条纹比 1.16/1.29(双线性)→ **1.00/0.93**(最近邻)。
- **38 轮补充(显示链两级结构)**:官方 `ImageViewer` 是**两级**——160×120 SDK
位图先用 `PaintFlagsDrawFilter + FILTER_BITMAP` 画进 **640×480 离屏**(4× 双线性),
SurfaceView 再放大到屏。中间 4× 对逐行 FPN 图样有 ~0.6× 低通:同一份 NUC 数据
走官方两级链复刻 0.12、走单级 12× 复刻 0.32(行交替,×1e-2 弧长)——**官方
"背景条纹更不明显"的主导机制是显示链**。Kotlin 侧已按此两级实现
(`RenderPipeline.nativeOut` 输出原生 160×120 + `LiveRenderer` 的 640×480 中间位图);
C 参考路径(`upscale2x`/`upscale_nearest`)保持不变,仅供黄金夹具与 PC 路径。
- 顺带(同一条链):**曲线插值温度要加 `BasePara2.fpaTempFix`**。官方 App 传给 SDK 的是
`帧尾 FPA + fpaTempFix`(真机 1699),`SelectCaliCurve`/`GenerateVolatileLens`
用它选/插值每像素标定表;温度换算链路仍用帧尾原值。C 侧**已实现**
(配置项 `fpa_temp_fix`,在 `rebuild_tables()` 的调用处加到帧尾 FPA 上;
两个 demo 设 1699 = 本机 BasePara2 实测值)。
2. **温度换算待移植**:官方对这台设备走的是"放射学分支"(每波段线性映射 + 环境温度误差修正
+ 发射率/透过率 revise),不是本 SDK 现在用的 T2E 表分支。完整反编译结论、伪代码与
参数来源见 `docs/android_app/temperature_chain_re_20261006.md`(§4d 可直接落地,
已用 15 帧官方同帧数据验证到平均 13.9 mC)。C 侧还需要:
- DDT 尾部辐射标定记录的解析(阈值/gain/offset/shift/±500/eps,偏移表见该文档 §4d);
- BasePara2 的 `ATZeroErrorPoint`/`ATErrorSlope`/`fpaTempFix`(线上偏移同样见该文档);
- 曲线 A 与本 SDK 的 `T2E` 表相同(646 项,已验证逐项一致)。
注:本机当前**没有 C 编译器**(gcc/clang 均缺失),以上 C 改动需在有编译器的环境里
编译 + 跑 csdk 测试后再提交。