文件

71 行
3.1 KiB
Python

"""Reproducible FAN-R2 power estimates, not startup or protection certification."""
import json
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
def calculate(selection):
fans_w = selection['quantity'] * selection['rated_at_pwm_100']['input_w']
fans_a = selection['quantity'] * selection['rated_at_pwm_100']['current_a']
rail_output_w = 5 * 4.01 * 4
buck_efficiency = 0.92
scenarios = []
# Auxiliary currents are example operating cases, not guaranteed maxima.
for name, aux_w in [('fans_and_rails_only', 0),
('aux_5v_0p5a_3v3_0p5a', (5 * .5 + 3.3 * .5) / .9),
('aux_5v_2p8a_3v3_0p5a', (5 * 2.8 + 3.3 * .5) / .9)]:
power_w = rail_output_w / buck_efficiency + fans_w + aux_w
for bus_v in (10.8, 12.0, 13.2):
scenarios.append({
'case': name, 'bus_v': bus_v, 'input_w_estimate': power_w,
'input_a_estimate': power_w / bus_v,
'headroom_to_10a_w': 10 * bus_v - power_w,
'rail_output_budget_at_8a_w': max(0, (8 * bus_v - fans_w - aux_w) * buck_efficiency),
})
return {
'mpn': selection['mpn'],
'certified': False,
'assumptions': {
'rail_output_w': rail_output_w,
'rail_conversion_efficiency': buck_efficiency,
'aux_conversion_efficiency': .9,
'adapter_current_limit_a': 10,
'provisional_continuous_current_target_a': 8,
'fan_input_model': 'Constant 12V-rated power for sensitivity only; voltage dependence is unknown',
'exclusions': ['input-path loss', 'startup peak', 'locked-rotor retries',
'maximum fan current', 'adapter temperature derating',
'efficiency tolerance', 'fan switch/receiver losses'],
},
'fan_rated_power_w': fans_w,
'fan_rated_current_a': fans_a,
'q7_conduction_at_25c_60mohm': {
'drop_v': fans_a * .06, 'loss_w': fans_a ** 2 * .06,
'limitation': '25C on-state only; no startup SOA, hot Rds(on), or short-circuit guarantee',
},
'c738_nominal_energy_at_13p2v_j': .5 * 22e-6 * 13.2 ** 2,
'c738_energy_limitation': 'Excludes both fan input capacitors and ceramic tolerance; does not bound peak current',
'scenarios': scenarios,
'thermal': {
'total_heat_target_w': 40,
'remaining_heat_budget_at_fan_rated_full_speed_w': 40 - fans_w,
'rise_k_at_6cfm_40w': 40 / (1200 * .00047195 * 6),
},
}
def main():
selection = json.loads((ROOT / 'fan-selection.json').read_text(encoding='utf-8'))
report = calculate(selection)
destination = ROOT / 'out/fan-power-budget.json'
destination.write_text(json.dumps(report, indent=2) + '\n', encoding='utf-8')
for row in report['scenarios']:
print(f'{row["case"]}: {row["bus_v"]:.1f}V, '
f'{row["input_w_estimate"]:.2f}W, {row["input_a_estimate"]:.3f}A, '
f'10A headroom {row["headroom_to_10a_w"]:.2f}W')
print(f'Conditional estimates only: {destination}')
if __name__ == '__main__':
main()