AnasSarkiz/NEMA14_RP2040

A NEMA motor assembly concept with a controller board, four proposed M3 screws and spacers, motor lead splices, and a JST 4-pin connector harness.

Version
1.13.0
License
ISC
Stars
0

scripts/restore-legal-via-sites.py

"""Restore legal baseline via sites with the revised drill/pad sizes.

Same-net annular copper may join its own SMT land, but the drill must stay
outside every SMT pad except the three declared thermal sites. Foreign
clearances are measured from the full copper land on every plated layer.
"""
import json,copy,math
from shapely.geometry import Point,LineString,box
from shapely.strtree import STRtree
import verify_supplier_connectivity as g
j=g.j;old=json.loads((g.ROOT/'artifacts/upright-revision/baseline-board.circuit.json').read_text());oldnames={e['subcircuit_connectivity_map_key']:e['name'] for e in old if e['type']=='source_net'};nets={e['name']:e for e in j if e['type']=='source_net'};stack=['top','inner1','inner2','bottom'];thermal=[]
for p in j:
 if p['type']=='pcb_smtpad' and p.get('width',0)>1 and p.get('height',0)>1 and g.src[g.comp[p['pcb_component_id']]['source_component_id']] in ['U_MCU','U_DRV','U_PD']:thermal.append((p['x'],p['y']))
static=[];owners=[]
for p in j:
 if p['type'] in ['pcb_smtpad','pcb_plated_hole']:static.append(g.geometry(p));owners.append(p)
 elif p['type']=='pcb_trace':
  for a,b in zip(p['route'],p['route'][1:]):
   if a['route_type']==b['route_type']=='wire' and a['layer']==b['layer']:static.append(LineString([(a['x'],a['y']),(b['x'],b['y'])]).buffer(max(a['width'],b['width'])/2));owners.append(p)
tree=STRtree(static);rows=[]
for original in old:
 if original['type']!='pcb_via':continue
 n=oldnames[original['subcircuit_connectivity_map_key']]
 if n in ['USB_DP','USB_DM','MCU_DP','MCU_DM','XIN','XOUT','XTAL_OUT']:continue
 v=copy.deepcopy(original);v['outer_diameter']=max(.45,v['outer_diameter']);v['hole_diameter']=max(.3,v['hole_diameter']);net=nets[n];v['source_net_id']=net['source_net_id'];v['subcircuit_connectivity_map_key']=net['subcircuit_connectivity_map_key'];v['source_trace_id']=next(e['source_trace_id'] for e in j if e['type']=='source_trace' and e.get('subcircuit_connectivity_map_key')==net['subcircuit_connectivity_map_key']);key=g.key(v);shape=Point(v['x'],v['y']).buffer(v['outer_diameter']/2);hole=Point(v['x'],v['y']).buffer(v['hole_diameter']/2);bad=False
 for i in tree.query(shape.buffer(.153)):
  p=owners[i];same=g.key(p)==key
  if same:
   if p['type']=='pcb_smtpad' and hole.intersects(static[i]) and not(n=='GND' and any(math.dist((v['x'],v['y']),c)<.001 for c in thermal)):bad=True;break
  elif shape.distance(static[i])<.153:bad=True;break
 if not bad:
  for p in j:
   if p['type']=='pcb_via' and g.key(p)!=key and shape.distance(Point(p['x'],p['y']).buffer(p['outer_diameter']/2))<.153:bad=True;break
 if bad:continue
 current=next((q for q in j if q.get('pcb_via_id')==v['pcb_via_id']),None)
 if current and math.dist((current['x'],current['y']),(v['x'],v['y']))<1e-9:continue
 if current:j.remove(current)
 j.append(v);rows.append({'via':v['pcb_via_id'],'net':n,'restored_baseline_xy_mm':[v['x'],v['y']]})
g.path.write_text(json.dumps(j,indent=2)+'\n');(g.ROOT/'artifacts/upright-revision/restored-via-sites.json').write_text(json.dumps({'construction_not_qualification':True,'sites':rows},indent=2)+'\n');print('Restored',len(rows),'baseline via sites with .30 mm minimum drills and .45 mm minimum lands')