abse/smartwatch-flex-top-only

Three-panel flex smartwatch with two parallel R3.9 bends, all components flat-top, low-profile JST connector harnesses, split heartbeat optical windows and screw-retained PCB mounting. Modeled 39 × 50.5 × 14.5mm prototype; 100% routing, zero native DRC and shorts. Hardware qualification pending.

Version
0.2.20
License
unset
Stars
0

scripts/analyze-electrical-corners.py

"""Recompute documented DC scenarios from the final native electrical source."""
from pathlib import Path
import hashlib,itertools,json,math
root=Path(__file__).resolve().parent.parent;o=root/'reports/electrical-fixes'
x=json.loads((o/'released-electrical.json').read_text());c=x['components'];a=json.loads((o/'electrical-analysis.json').read_text())
res=lambda n:c[n]['resistance']
net=lambda n,p:next(v['net'] for v in c[n]['pins'] if v['pin']==p)
assert c['U10']['part']=='TLV75733PDBVR' and c['U21']['part']=='TPS3840DL31DBVR'
assert all(net(n,p)=='INPUT_5V' for n,p in [('U17',5),('U18',5),('R12',1),('R13',1),('R14',1),('R17',1),('C36',1),('C37',1)])
assert all(net(n,p)=='RAW' for n,p in [('U21',2),('R59',1),('C76',1),('U7',10)])
assert net('U21',1)==net('U7',1)=='BAT_OK'
upper=sum(res(n) for n in ['R1','R2','R3']);lower=res('R4')
a['vsysNominalV']=.5*(1+upper/lower)
a['vsysCornerRangeV']=[.5*.99*(1+upper*.99/(lower*1.01)),.5*1.01*(1+upper*1.01/(lower*.99))]
a['tlv757MaximumOutputPlusMaxDropoutV']=3.3*1.015+.475
a['minimumHeadroomMarginV']=a['vsysCornerRangeV'][0]-a['tlv757MaximumOutputPlusMaxDropoutV']
assert res('R30')==res('R31')==2200
a['i2c400kHzMaxCapacitancePFWith2k2And300nsRise']=300e-9/(.8473*res('R30'))*1e12
a['chargeTerminationTypicalA']=.030*res('R58')*(1/res('R5')+1/res('R6')) # TI typical K_ITERM; not a guaranteed trip
assert math.isclose(a['chargeTerminationTypicalA'],.0036382978723404256,rel_tol=1e-8)
a['minimumCutoffV']=3.1*.985;a['maximumCutoffV']=3.1*1.015
a['expectedRestartRangeV']=[a['minimumCutoffV']+.175,a['maximumCutoffV']+.225]
for label,hi,lo in [('Cold',res('R14'),res('R15')+res('R16')),('Hot',res('R17')+res('R18'),res('R19')+res('R20'))]:
    ratios=[lo*.99/(hi*1.01+lo*.99)-.025/4,lo*1.01/(hi*.99+lo*1.01)+.025/4]
    rr=[res('R13')*.99*ratios[0]/(1-ratios[0]),res('R13')*1.01*ratios[1]/(1-ratios[1])]
    a['targetBatteryNtcResistance'+label+'TripOhms']=rr
    temps=[1/(1/298.15+math.log(r/r25)/b)-273.15 for r,r25,b in itertools.product(rr,[9900,10100],[3435*.99,3435*1.01])]
    a['targetBatteryNtc'+label+'TripC']=[min(temps),max(temps)]
assert a['minimumHeadroomMarginV']>.06
f=root/x['source'];assert hashlib.sha256(f.read_bytes()).hexdigest()==x['sha256']
a['sourceFile']=x['source'];a['sourceSha256']=x['sha256'];a['calculationInputs']={n:{'part':c[n].get('part'),'resistance':c[n].get('resistance')} for n in ['U10','U21','R1','R2','R3','R4','R5','R6','R13','R14','R15','R16','R17','R18','R19','R20','R30','R31','R58']}
(o/'electrical-analysis.json').write_text(json.dumps(a,indent=2)+'\n');print('PASS: final source roles and DC scenarios; physical limits unchanged')