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/audit-mount-installation.py
"""Validate real screw insertion and PH0 driver access before fitting the display."""
from pathlib import Path
import json,base64,io,numpy as np,trimesh
from mount_mesh_utils import numerical_face_contact
root=Path(__file__).resolve().parent.parent
h=json.loads((root/'mechanical/mount-hardware.json').read_text())
parts=[]
def load(file,shift=(0,0,0)):
p=root/f'mechanical/{file}.glb'
if p.exists():scene=trimesh.load(p,force='scene')
else:
b=base64.b64decode((root/f'mechanical/{file}.ts').read_text().split('base64,')[1].split('"')[0])
scene=trimesh.load(io.BytesIO(b),file_type='glb',force='scene')
for n in scene.graph.nodes_geometry:
T,k=scene.graph[n];m=scene.geometry[k].copy();m.apply_transform(T);m.apply_translation(shift);parts.append((file+'/'+n,m))
for f in ['case-body','upright-pcb','pcb-cradle','pcb-retainers','relocated-motor','optical-windows','optical-light-seal','temperature-probes','motor-inline-connector','button-actuator','motor-support-shim','battery-harness','haptic-harness','battery-ntc-harness','coil-ntc-harness','motor-factory-leads']:load(f)
load('battery-envelope',(3,-.19159126281738,-3.6));load('ferrite-envelope',(0,-8.99159126281738,-6.78))
def collisions(mesh,objects):
result=[]
for name,other in objects:
if np.any(np.maximum(mesh.bounds[0]-other.bounds[1],other.bounds[0]-mesh.bounds[1])>=-.000005):continue
manager=trimesh.collision.CollisionManager();manager.add_object(name,other)
_,_,data=manager.in_collision_single(mesh,return_names=True,return_data=True)
for c in data:
if c.depth>.000005:result.append((name,c))
return result
failures=[];mounts=[]
single=trimesh.load(root/'mechanical/m1-6x6-iso7046.glb',force='scene').to_mesh()
for mount in h['mounts']:
x,y=mount['x'],mount['y'];head=h['headTopZMM'];tip=head-h['overallLengthMM']
assert np.allclose(single.bounds[:,2],[-6,0],atol=.000005)
engagement=h['postTopZMM']-tip;bottom_clearance=tip-h['pilotBottomZMM'];depth=h['postTopZMM']-h['pilotBottomZMM']
assert engagement>=2.5 and bottom_clearance>=.8
assert h['minimumFinishedThreadDepthMM']>=engagement and depth-h['minimumFinishedThreadDepthMM']>=2*h['pitchMM']-.000005
tool=trimesh.creation.cylinder(radius=h['driverShaftDiameterMM']/2,height=20,sections=48)
tool.apply_translation([x,y,head+.05+10])
for name,c in collisions(tool,parts):failures.append({'mount':mount['name'],'phase':'3mm PH0 shaft access, display absent','body':name,'depthMM':float(c.depth)})
# Every point in the path includes all components and factory harnesses.
for offset in np.arange(0,8.001,.2):
moving=single.copy();moving.apply_translation([x,y,head+offset])
hits=collisions(moving,parts)
hit_names={name for name,_ in hits}
face_contacts={name for name in hit_names if name.startswith('pcb-retainers/') and numerical_face_contact(moving,next(m for n,m in parts if n==name))}
for name,c in hits:
if name in face_contacts:continue
point=np.array(c.point)
engagement_contact=name.startswith('case-body/') and np.linalg.norm(point[:2]-[x,y])<=h['diameterMM']/2+.000005 and h['pilotBottomZMM']-.000005<=point[2]<=h['postTopZMM']+.000005
if not engagement_contact:failures.append({'mount':mount['name'],'phase':'screw insertion','heightAboveSeatedMM':float(offset),'body':name,'depthMM':float(c.depth),'pointMM':point.tolist()})
mounts.append({'name':mount['name'],'centerMM':[x,y,head],'threadEngagementMM':engagement,'blindPilotDepthMM':depth,'screwTipToPilotBottomMM':bottom_clearance,'minimumFinishedThreadDepthMM':h['minimumFinishedThreadDepthMM'],'PH0DriverShaftDiameterMM':h['driverShaftDiameterMM'],'insertionPositionsChecked':41})
report={'status':'PASS' if not failures else 'FAIL','mounts':mounts,'failures':failures,'method':'FCL screw sweep and 3mm driver shaft clearance against the actual finished printed case, populated folded PCB, cradle, front arms, battery/ferrite, peripherals and all five harnesses. Top bezel and display are not fitted at this assembly stage. Imported mating-face alarms require an independent Boolean overlap below 1e-6mm3 with every overlap vertex within 5um of both surfaces. Only thread engagement inside each local blind post bore is intentional.','assemblyOrder':['Print/finish the empty case; drill 1.3mm pilots 3.8mm deep and use an M1.6 x 0.35 bottoming tap for at least 3.1mm finished thread depth. Clean all debris before electronics installation.','Fit the stiffened panel into the cradle rear roofs, fold and connect factory-crimped harnesses. Lower the assembly onto four integral case posts.','Install both front retaining arms and purchased ISO7046 M1.6 x 6 screws from above with a slim PH0 driver; screw tips stop 0.9mm above the blind pilot bottoms.','Fit/bond the top bezel on its 0.1mm reserved seam and fit the display after the screws are tightened. Access for later service requires opening the display/bezel closure.'],'qualification':'Nominal fit and tool envelope; actual PH0 tool profile, supplier hardware tolerances and printed thread grip require coupon/prototype tests. Screw recess is illustrative CAD, not drive-gauge manufacturing data.'}
(root/'reports/mount-installation.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps({k:v for k,v in report.items() if k!='failures'},indent=2));print('Failures:',len(failures),failures[:5]);assert not failures,'Screw installation or driver access fails'