From e80d62050b6e2259630102952b50a562d3fdc6e9 Mon Sep 17 00:00:00 2001 From: ZXCLI Date: Tue, 6 Oct 2026 02:52:25 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E6=B8=A9=E5=BA=A6=E9=93=BE=E9=97=AD?= =?UTF-8?q?=E5=90=88=E2=80=94=E2=80=94=E5=AE=8C=E6=95=B4=E6=A8=A1=E5=9E=8B?= =?UTF-8?q?=E4=B8=8E=E5=AE=98=E6=96=B9=E8=BE=93=E5=87=BA=E5=B9=B3=E5=9D=87?= =?UTF-8?q?=E5=B7=AE=2013.9mC(0.014C)=EF=BC=9B=E6=A0=B9=E5=9B=A0=E6=98=AF?= =?UTF-8?q?=20Hex-Rays=20=E5=8D=81=E8=BF=9B=E5=88=B6=E5=AD=97=E6=AE=B5(dev?= =?UTF-8?q?+0xA4)=E8=AF=AF=E8=AF=BB=EF=BC=9B=E9=99=84=E5=8F=AF=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E8=90=BD=E5=9C=B0=E7=9A=84=E4=BC=AA=E4=BB=A3=E7=A0=81?= =?UTF-8?q?+DDT=E5=B0=BE=E9=83=A8=E5=B8=83=E5=B1=80+=E5=89=A9=E4=BD=99?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=BA=90=E6=B8=85=E5=8D=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../temperature_chain_re_20261006.md | 131 ++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/docs/android_app/temperature_chain_re_20261006.md b/docs/android_app/temperature_chain_re_20261006.md index 4d2713a..4a2f2a4 100644 --- a/docs/android_app/temperature_chain_re_20261006.md +++ b/docs/android_app/temperature_chain_re_20261006.md @@ -141,6 +141,137 @@ Tout = Teff + tab[idx] + (tab[idx+1]-tab[idx])*(dev+0x5C-5000*(idx+3))/5000 再采一次,观察官方输出的变化量,反推该项的正确组合方式。 3. 钉死后即按 §5 落地 Kotlin,并用本轮同帧数据做逐像素回归(band 1 目标 mC 级)。 +## 4c. 第三轮:多帧序列 + 相关性分析(当前状态:差一个约 0.15 ℃ 的静态项) + +采集 15 帧同帧配对样本(`offseries.pkl`,脚本 `offseries4.py`:只要求 counts+记录稳定, +其余字段"读到时记录"),逐帧反解"官方隐含波段映射 vs 记录波段映射"的差: + +- 差值不是常量而是 **在 −123…−184 mC 之间变化**,并且 + **与 `dev+0x5C` 的相关系数 r = 0.999(斜率 ≈ 1:1)**——`dev+0x5C` 正是反编译里 + 环境补偿 v50 的输入之一(`v50 = dev+0x58 + 2*(dev+0x5C - dev+0x54) - dev+0x60 + v47`, + 实测 v50 = 30 / 60 / 89,随帧相位阶跃 30)。 +- 逐项复算(按反编译):`v45 = 34120`、`v47 = 0`、`v50 = 30…89`、`v52 = v53 = 44`、 + `v54 = 11…33`,对应 `A40` 的温度增量 +30…+90 mC;而"缺的量"是 + **≈ +123…+181 mC,方向相反**(即官方比"含环境项的模型"还要高 ~211 mC), + 用最小二乘把它写成 `Δ ≈ 210.8 − 0.98·v50`(15 帧内拟合残差 ±3 mC)。 +- 该"211"**不是存储常量**:设备对象 0x0–0x1200、记录 540B、DDT 尾部 560B 里都搜不到 + 211/−211,也没有 0.05 附近的浮点;`dev+0x14 = 20000`(20 ℃)、`dev+0x50 = 200` + 是目前仅有的两个可疑字段(后者与 211 接近,需单变量验证)。 +- 另一个可能的混杂因素:`dev+0x5C/0x54/0x58/0x60` 每帧都在变(阶跃 30),而我的 + 读取时刻与 SDK 计算温度的时刻可能差一帧——`v50` 的"追踪"信号有可能来自这个相位差。 + +**当前结论(第三轮收敛后)**:结构 100 % 确认、斜率逐位吻合;把 15 帧的差值写成闭环形式后 +是 **`official = model(含环境项) − 211.3 mC`**(15 帧内该项稳定在 −211.3 ± 1 mC, +与 v50 无关;v50 的"追踪"只是我早期把 Δ 写成"v50 − 211.3"造成的表象)。 +也就是说:**只剩一个 −211.3 mC(≈0.21 ℃)的静态项没定位**,且它**不在任何数据文件里** +(设备对象 0x0–0x1200、记录 540B、DDT 尾部 560B 均无 211/−211,也无 0.05 量级浮点); +按反编译算出的距离/大气项是 **−995 mC**(≈4.7 倍),说明该段(`dev+0xA8` 那一支)还有 +一段没读全(可能是 `dev+0xA8` 之外的组合,或该项的 `sub_180015940` 首参不是常量 35000)。 + +工程判断:这已经是"+0.09 ℃(常温)…−0.70 ℃(高温)"→"−0.21 ℃ 常数"的差距,但**未定位前不改代码** +(否则就是把 −211.3 这个拟合常数硬编码进去,违反"不猜"原则)。 + +## 4d. 第四轮:**模型闭合(平均 13.9 mC)**——根因是 Hex-Rays 的十进制/十六进制陷阱 + +第三轮卡住的 −211.3 mC 常数,来源已找到:`sub_180016290` 距离/大气段的汇编是 + +``` +movss xmm1, [rsi+0A8h] ; dev+0xA8 (float) +mov ecx, [rsi+44CB8h] ; 当帧均值温度 v56 +... clamp v56 到 [5000,40000](或 ±80000)... +sub ecx, [rsi+0A4h] ; ★ 减的是 dev+0xA4(十进制 164 = 0xA4) +mov edx, 88B8h ; 35000 +cvtsi2ss xmm0, ecx ; mulss xmm0, xmm1 ; cvttss2si r8d, xmm0 +call sub_180015940 ; 温度域小量 → 辐亮度域增量 +add ebx, eax ; 累加到 v54 +``` + +Hex-Rays 伪代码里写的是 `*(_DWORD *)(a1 + 164)`(**十进制** 164),我上一轮误当成 +十六进制 `0x164`(=0,因此那一项被算成 0)——**正确字段是 `dev+0xA4`(实测 22216 mC)**: + +``` +arg = (int)((float)(clamp(v56, [5000,40000]) - dev+0xA4) * dev+0xA8) + = (29831 - 22216) * (-0.0279) = -212 +dist = sub_180015940(35000, arg) # ≈ -75(辐亮度域) +v54 += dist # 再经 A40 变回温度域 ≈ -212 mC +``` + +**修正后,完整模型与官方输出(15 帧 × 19200 像素)平均 |Δ| = 13.9 mC(0.014 ℃)**, +全部 15 帧一致(13.4–14.5 mC);残余多为 Python double vs C float32 取整差与跨帧毛刺, +最大 ~950 mC 只在极少数像素(band 边界/跨帧)。 + +→ 同时也解释了之前"数值对不上"的另一半原因:`dev+0xA4` 我根本没有读到过 +(它不在 DDT 里,是设备上报的运行参数)。 + +### 已完整验证的算法(可直接落地,逐条对应反编译) + +```text +A[0..645] = 曲线 A(= 我们 OfficialTables.T2E,已验证前 8 项一致;需再核对全表) +S[i] = (0x1000000 + (A[i+1]-A[i])/2) / (A[i+1]-A[i]) # 官方 0x180099A20 表,我们按需自算 +cdiv(a,b) = 向零截断的整数除法(C 语义) + +E2T(x) = 曲线A正向:i=clamp((x+150000)/4096,0,645); max(A[i] + (A[i+1]-A[i])*(x+150000-(i<<12))/4096, 0) +iE2T(L) = 曲线A逆向:i=最大使 A[i]<=L; ((L-A[i])*S[i])>>12 + (i<<12) - 150000 +A40(T,d) = 在 T 处把"辐亮度域增量 d"换算成"温度域增量"(含跨段与 2|d| 判定) +A940(T,d)= 同上,但斜率恒用 (A[i+1]-A[i]) + +band_map(c): + b = 最大的 i 使 c > thr[i](thr/thr_cnt 在记录 rec+0x40/rec+0x3C,8 段) + return rec[0xD8+8b] + ((c * rec[0xD4+8b]) >> rec[0x30]) # (offset, gain, shift=12) + +温度(c)(MAG_GetTemperatureData flag=1 路径): + v46 = band_map(c) + v45 = dev[0x54] + rec[0x38] - rec[0x34] + v47 = dev[0x64]!=0 ? clamp(v45-dev[0x58], -5000, 1000) : clamp(dev[0x58]-v45, -1000, 1000) + v50 = dev[0x58] + 2*(dev[0x5C]-dev[0x54]) - dev[0x60] + v47 + v49 = dev[0x58] + 150000; i52=clamp(v49/4096,0,645); i53=clamp((v49+v50)/4096,0,645) + v54 = (i52==i53) ? v50*(A[i52+1]-A[i52])/4096 + : A[i53] + (A[i53+1]-A[i53])*(v49+v50-(i53<<12))/4096 + - (A[i52+1]-A[i52])*(v49-(i52<<12))/4096 - A[i52] + v56 = clamp(dev[0x44CB8], 5000..40000) # 当帧均值温度(SDK 每帧算) + v54 += A940(35000, (int)((v56 - dev[0xA4]) * dev[0xA8])) + T = v46 + A40(v46, v54) + # 调用方的 revise(flag=1 时): + K = dev[0xF8] * dev[0xF4] * dev[0xD4] # 实测 1.0 × 0.99991 × 0.98 = 0.979912 + Lamb = E2T((int)(dev[0xD8] * 1000.0f)) # dev[0xD8] = 当帧均值温度(℃),实测 = 44CB8/1000 + T = max(iE2T((int)((E2T(T) - (1-K)*Lamb) / K)), -273150) + idx = clamp((dev[0x5C]-15000)/5000, 0, 6) # 环境温度查表(本机 dev[0xAC..0xC8] 全 0) + T += tab[idx] + (tab[idx+1]-tab[idx])*(dev[0x5C]-5000*(idx+3))/5000 + if dev[0x6C] != 0: T += (int)(T*dev[0xEC]) - (int)(dev[0xF0] * -1000.0f) +``` + +### DDT 尾部 = 标定记录(已完整对齐,App 可直接解析) + +相对 `DDT_size - 560`(本机 `official.ddt` = 1856416,故起点 1855856): + +| 尾部偏移 | 值(本机) | 含义 | +|---|---|---| +| +0x34 | 0.0065f | (未用,待确认) | +| +0x38 / +0x3C / +0x40 | 1.0f / 2.0f / 1.0f | (未用,待确认) | +| +0x44 | 12 | gain 右移位数(rec+0x30) | +| +0x48 / +0x4C | 29417 / 29917 | rec+0x38 / rec+0x34(相差 500,环境补偿用) | +| +0x50 | 8 | 波段数(rec+0x3C) | +| +0x54 … +0x70 | 9389,9995,10328,10581,10797,11160,11430,11772 | 8 个波段阈值(rec+0x40…) | +| +0xE8 … +0x124 | (33486,−51932) … (35059,−56391) | 每波段 (gain, offset)(rec+0xD4…) | +| +0x1B0 … +0x1D0 | 11,−2,−16,−25,−31,−36,−45,−52,−61 | (未用,待确认) | +| +0x218 | 1.0f | 发射率(rec+0x204) | +| +0x21C / +0x220 | 19790 / 44930 | rec+0x208 / rec+0x20C(距离项阈值对) | + +### 落地到 App 还缺的数据来源(下一步) + +| 运行时字段 | 值(本机) | 来源(已定位/待定) | +|---|---|---| +| `dev+0xA4` | 22216 mC | **官方手机 App 的 `ExtPara.intCurrentEnvTemperature`**(`DeviceController.getExtParameter` JNI;我们的 `IrSession` 目前只取 BasePara1/2,需要补这个命令) | +| `dev+0x44CB8` | 当帧均值温度 | 可自算:`band_map(s_mean)` + 环境项(flag=0 路径) | +| `dev+0xD8` | = 44CB8/1000(℃) | 同上 | +| `dev+0x54 / 0x58 / 0x5C / 0x60` | 34620/34120/34650/34150 | 传感器/环境温度对;0x58=0x54−500(=rec 差);0x5C−0x54=30 → 需再用"与帧数据相关性"定位(候选:帧尾 FPA、BasePara2 的 `fpaTempFix`/`ATZeroErrorPoint`) | +| `dev+0xF4 / 0xF8` | 0.99991 / 1.0 | 修正系数;候选 `BasePara2.ATErrorSlope`/`ATZeroErrorPoint` | +| `dev+0x64 / 0x6C / 0xCC` | 1 / 1 / 2 | 开关位,需与 ExtPara/BasePara2 字段对照 | + +**下一步**:把上表右列补全(用同一套"同帧相关性"方法:把候选来源的字段与 dev 字段逐帧对齐), +然后按上面的伪代码在 Kotlin 落地(波段映射 + 环境补偿 + 距离项 + revise), +用 15 帧官方同帧数据做逐像素回归(目标 ≤20 mC),最后替换 App 的所有温度读数点。 + ## 5. 我们 App 侧要改什么(等模型钉死后再动手) 1. `TempMath` 增加"官方放射学分支"实现:波段映射(参数取自 DDT 尾部)+ §3 修正;