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/bridge-local-power-islands.py

import sys,json,math
sys.path.insert(0,'scripts')
import verify_supplier_connectivity as g
from shapely.geometry import Point,LineString,box
from shapely.strtree import STRtree
net=next(k for k,v in g.nets.items() if v=='V3V3');stack=['top','inner1','inner2','bottom'];shapes=[];owners=[]
for e in g.j:
 typ=e['type']
 if typ in ['pcb_smtpad','pcb_plated_hole']:
  if 'inner1' in e.get('layers',[e.get('layer','top')]):shapes.append(g.geometry(e));owners.append(e)
 elif typ=='pcb_via':shapes.append(Point(e['x'],e['y']).buffer(e['outer_diameter']/2));owners.append(e)
 elif typ=='pcb_trace':
  for a,b in zip(e['route'],e['route'][1:]):
   if a['route_type']==b['route_type']=='wire' and a['layer']==b['layer']=='inner1':shapes.append(LineString([(a['x'],a['y']),(b['x'],b['y'])]).buffer(max(a['width'],b['width'])/2));owners.append(e)
 elif typ=='pcb_keepout':
  c=e['center'];r=e['radius'];shapes.append(box(c['x']-r,c['y']-r,c['x']+r,c['y']+r));owners.append(e)
 elif typ=='pcb_hole':shapes.append(Point(e['x'],e['y']).buffer(e['hole_diameter']/2+.1));owners.append(e)
tree=STRtree(shapes);es,rs=g.groups(net);lands={}
for e,r in zip(es,rs):
 if e.get('pcb_port_id'):lands.setdefault(r,[]).append(e)
best=None
for ra in lands:
 for rb in lands:
  if ra>=rb:continue
  va=[e for e,r in zip(es,rs) if r==ra and e['type']=='pcb_via'];vb=[e for e,r in zip(es,rs) if r==rb and e['type']=='pcb_via']
  for aa in va:
   for bb in vb:
    a=(aa['x'],aa['y']);b=(bb['x'],bb['y'])
    for pts in [[a,b],[a,(b[0],a[1]),b],[a,(a[0],b[1]),b]]:
     length=LineString(pts).length
     if length>15:continue
     geom=LineString(pts).buffer(.08);hits=set();bad=False
     if not box(-17.15,-17.15,17.15,17.15).covers(geom):continue
     for i in tree.query(geom.buffer(.153)):
      e=owners[i]
      if g.key(e)==net or geom.distance(shapes[i])>=.153:continue
      if e['type']!='pcb_trace' or e['pcb_trace_id'].startswith('feedback_fanout_') or g.nets.get(g.key(e)) in ['PD_VBUS','A_PLUS','A_MINUS','B_PLUS','B_MINUS','SENSE1','SENSE2','GND','LOGIC_IN','BUCK_SW','BUZZER_SINK']:bad=True;break
      hits.add(e['pcb_trace_id'])
     if bad or len(hits)>2:continue
     cost=len(hits)*100+length
     if best is None or cost<best[0]:best=(cost,aa,pts,hits)
if best is None:print('NO LOCAL BRIDGE');raise SystemExit(1)
_,via,pts,hits=best;print('LOCAL BRIDGE',pts,'RIP',sorted(hits),flush=True)
if '--apply' in sys.argv:
 g.j[:]=[e for e in g.j if e.get('pcb_trace_id') not in hits];uid='reviewed_local_v3_bridge'
 while any(e.get('pcb_trace_id')==uid for e in g.j):uid+='_next'
 base={k:via[k] for k in ['source_net_id','source_trace_id','subcircuit_connectivity_map_key','subcircuit_id'] if k in via}
 g.j.append({'type':'pcb_trace','pcb_trace_id':uid,'route':[{'route_type':'wire','x':x,'y':y,'layer':'inner1','width':.16} for x,y in pts],'pcb_port_ids':[],**base});g.path.write_text(json.dumps(g.j,indent=2)+'\n')
 out=g.ROOT/'artifacts/reference-feedback/local-power-bridges.json';history=json.loads(out.read_text()) if out.exists() else [];history.append({'trace':uid,'points_mm':pts,'removed_foreign_trace_ids':sorted(hits),'independent_physical_branch_cut_and_thermal_screen_required':True});out.write_text(json.dumps(history,indent=2)+'\n')