from pathlib import Path
import hashlib,json,zipfile,re
R=Path(__file__).resolve().parent;B=R.parent;H=B/'engineering_handoff_local';T=R/'firmware/固件/lamp_target';sha=lambda p:hashlib.sha256(p.read_bytes()).hexdigest()
build=json.loads((T/'build/build_verification.json').read_text());assert build['candidate_NTC_curve_bound'] and not build['led_output_enabled'] and not build['physical_NTC_identification_and_calibration']
curve=json.loads((R/'curve_source.json').read_text());contract=json.loads((R/'NTC_part_and_harness_contract.json').read_text())
source_hash=sha(R/'Vishay_NTCLE100_29049.pdf');log=(R/'actual_NTC_test.log').read_text();assert '3842 nominal curve codes' in log
max_error=float(re.search(r'max error ([\d.]+)C',log)[1]);assert 'Assertion' in (R/'wrong_curve_NTC_test.log').read_text()
checks={'status':'A45_EXACT_NTC_CANDIDATE_NOMINAL_CURVE_LOCAL_PASS_FABRICATION_HOLD','candidate_MPN':curve['exact_candidate_MPN'],'R25_ohm':10000,'nominal_curve_bounds_C':[-40,125],'curve_valid_ADC_bounds':[134,3975],'curve_knots':166,'tested_ADC_codes':4096,'valid_curve_codes':3842,'algorithm_nominal_inverse_max_difference_C':max_error,'difference_scope':'Software LUT/rounded-code versus manufacturer inverse equation only; not sensor or assembly accuracy','numerical_measurement_cases':36855,'ADC_measurement_NTC_integration_cases':72,'build':build,'outputs_enabled':False,'thermal_thresholds_qualified':False,'installed_sensor_identified':False,'physical_verified_requirements':0,'reference_requirements':20,'source_pdf_sha256':source_hash}
(R/'checks.json').write_text(json.dumps(checks,indent=2)+'\n')
(R/'source_references.json').write_text(json.dumps({'manufacturer_PDF':'https://www.vishay.com/docs/29049/ntcle100.pdf','PDF_revision':'07-May-2025','PDF_sha256':source_hash,'source_pages':[1,2,4,10],'material_row':9,'exact_MPN_manufacturer_listing':'https://www.vishay.com/en/product/29049/tab/quality/','source_scope':'Manufacturer nominal characteristic, ordering and mounting data; none is an installed assembly validation'},indent=2)+'\n')
report=f'''# 灯具精确NTC候选与温度查表 A45

状态：器件候选/曲线/目标固件实现及本地检查完成，FABRICATION_HOLD。温度数值为指定NTC与理想10k上拉条件下的诊断估计，没有整机热校准，也没有授予LED许可。三款20项功能效果验收仍0项实测确认。

## 器件与接线落实

选择外接Vishay NTCLE100E3103GB0A候选，10kΩ@25°C、R25±2%、B25/85=3977K±0.75%。现有安全板A39 J4.4=NTC_ADC、J4.2=GND已经逐脚网表核对；无极性两线连接，保留J4其他信号，不需要新增传感器PCB。采用2%候选用于降低未校准温度偏差，实际千台价格和供应仍未报价，不声称比替代型号更便宜。

暂定用干式电气隔离夹持固定在实测最热LED板附近，位置/夹持件/导热接触仍需实机定型。厂商说明外涂层没有绝缘性能保证且不适用于灌封；不能把器件包封层当作绝缘，不能直接假定胶灌封安装合法。候选结构需要验证绝缘夹持与热响应，而不是把裸元件数据当装配合格。

## 已接入的真实源代码

使用厂商29049 Rev07-May-2025第4页material9的完整四系数温阻公式生成-40至125°C、1°C间隔166点。生成器和CSV保留，`generate_curve.py --check`可复现固件查表。没有用固定B=3977替代全范围曲线。

查表按10k同参考供电上拉映射12bit采样码；有效nominal raw134–3975。二分最多约8次，整数插值，不在目标运行时调用log/exp或引入浮点库。范围外/空参数拒绝且不改输出，返回的是条件诊断温度，不能证明已安装正确器件。主循环安全采样在A44原始帧和诊断成功后调用，更新`lamp_debug_candidate_ntc_centic/valid`；失败清valid，旧温度可能保留，必须同时读valid和时间戳。

R10目前只知道名义10k，精确MPN/公差尚未选定；其容差、ADC偏差、供电网络压差和线缆影响没有从温度结果消除。更换其他10kNTC也不能继续使用这套曲线；温度阈值和temperature_ok仍未绑定。

## 当前验证

- 全4096个ADC码验证，3842个在候选曲线范围内：单调性、边界、空指针、错误返回不改输出通过。与厂商独立反算方程比较最大差异{max_error:.6f}°C，仅是1°C查表/码值取整/反算公式间的软件近似，绝不是器件或整机精度声明。
- 故意扭曲插值比例的合成负对照被反算误差断言拒绝；该对照不是实体传感器故障证明。
- 72组实际ADC驱动/寄存器模型→完整帧→参考/电流/电压诊断→NTC温度换算的主机集成通过。模拟10k分压中点得到约25°C，仍是模拟输入，不是室温实测。
- 36,855组原有名义诊断数值检查、ADC时序、IR/启动/guard/票据/UART/UI/输出适配器回归以及既有负对照全部通过，ASan/UBSan通过。
- Cortex-M0+完整构建、main到温度换算的调用路径静态核对通过。BIN {build['flash_binary_bytes']} B，FLASH16928/32768、RAM2832/8192（静态链接值，未测栈/CPU峰值），BIN SHA256 `{build['sha256_bin']}`。

## 温度到产品效果仍有实机门槛

器件资料给出的约15s热时间常数和7mW/K耗散因子仅供参考，不能当作本装配响应。按3.6V/10k上拉静态算例，NTC最大自热耗散为0.324mW；用参考耗散因子估算约0.0463°C，不包括实际夹持/封装/空气流动，也不保证这个温升。NTC的±2%/B公差与R10/ADC/装配偏差应另建完整误差预算；不能用0.123°C算法误差代替。外部THERM_OK独立热保护路径仍需实测，不能把本NTC数值当作唯一过温关断保障。

本轮没有实际装上传感器、没有温度校准或热阶跃/脱落/开短路响应证据。`NTC_thermal_measurements_blank.csv`的实测字段全部留空。后续对25/40/60/85/110°C及加热/冷却、引线开短路、接触脱落做受控台架验证，并在实际光学固化/热与AI链路测试中设定阈值和安装位置；本资料不授权直接通电。

硬件仍配A43电源+A39安全，未改CAD。外围新增候选1个NTC及安装/线束需求，整机BOM与每款1000台成本仍需报价。目标固件A45 LED关断、guard未配置/授权，温度/供电/驱动健康许可仍false。还需驱动状态回读和校准输出闭环，以及电磨/吸尘器两款完整功能效果和实机证据，不能由此温度查表替代。

完整可编辑源码、ELF/BIN/MAP、厂商PDF/曲线生成器、测试/日志和SHA清单已打包。运行`sh run_checks.sh`需要cc/Python3/arm-none-eabi工具链。未采购、外联、烧录、通电或制造下单。
'''
(R/'README.md').write_text(report);(H/'A45-lamp-NTC-part-and-curve.md').write_text(report)
files=[p for p in R.rglob('*') if p.is_file() and p.name!='package.py' and not p.name.startswith('.') and all(x not in p.parts for x in ['host_test','adc_host_test','measurement_host_test','ntc_host_test']) and p.suffix in ['.c','.h','.s','.ld','.sh','.py','.md','.json','.log','.txt','.csv','.elf','.bin','.map','.pdf']]
manifest=''.join(f'{sha(p)}  {p.relative_to(R).as_posix()}\n' for p in sorted(files));(H/'A45-SHA256SUMS.txt').write_text(manifest)
z=H/'Lamp-exact-NTC-candidate-curve-review-A45.zip'
with zipfile.ZipFile(z,'w',zipfile.ZIP_DEFLATED) as a:
 for p in files:a.write(p,p.relative_to(R).as_posix())
 a.writestr('SHA256SUMS.txt',manifest)
with zipfile.ZipFile(z) as a:
 assert a.testzip() is None
 for line in manifest.splitlines():
  digest,name=line.split('  ',1);assert hashlib.sha256(a.read(name)).hexdigest()==digest
 count=len(a.namelist())
ip=H/'LOCAL-PACKAGES.json';idx=json.loads(ip.read_text());idx.update(status=checks['status'],latest_report='A45-lamp-NTC-part-and-curve.md',latest_machine_report='../lamp_NTC_A45/checks.json',latest_manifest='A45-SHA256SUMS.txt')
e={'archive':z.name,'sha256':sha(z),'bytes':z.stat().st_size,'zip_entries':count,'manifest_entries':len(files),'zip_crc':'PASS','manifest_readback':'PASS','manufacturing_status':'FABRICATION_HOLD'}
idx['packages']=[p for p in idx['packages'] if p.get('archive')!=z.name]+[e]
idx['latest_lamp_safe_firmware_A45']={'source':'../lamp_NTC_A45/firmware/固件/lamp_target','binary_sha256':build['sha256_bin'],'board_pair':'A43 power+A39 safety','NTC_candidate_MPN':'NTCLE100E3103GB0A','nominal_curve_bound':True,'NTC_installed_and_calibrated':False,'thermal_thresholds_bound':False,'outputs_enabled':False,'physical_test_release':False}
idx['latest_lamp_power_frontend_A43']['firmware_pair']='A45 conditional NTC curve plus factory-reference nominal diagnostics only'
idx['latest_lamp_IR_hardware_A39']['bound_firmware_candidate']='../lamp_NTC_A45/firmware/固件/lamp_target'
idx['latest_lamp_IR_policy_A35']['linked_candidate']='../lamp_NTC_A45/firmware/固件/lamp_target'
idx['lamp_clean_build']={'report':'../lamp_NTC_A45/firmware/固件/lamp_target/build/build_verification.json','binary_sha256':build['sha256_bin'],'physical_validation':'NOT_RUN_NO_SAMPLE','led_output_enabled':False,'scope':'A45 PCA hard-off image with selected conditional NTC nominal curve; assembly/temperature/supply/driver safety closure incomplete'}
idx['manufacturing_exclusions']['lamp_A45']='Exact NTC candidate/nominal curve implemented and host+ARM checked; R10 exact tolerance, actual sensor mounting/thermal calibration, physical driver/output and product effects unverified; FABRICATION_HOLD.'
ip.write_text(json.dumps(idx,ensure_ascii=False,indent=2)+'\n');print(json.dumps(e,indent=2))
