MustafaMulla29/rp2040-bldc-motor-controller-new
A USB‑C PD–powered RP2040 BLDC motor controller PCB with 3.3 V logic, MCT8329A sensorless commutation, three MOSFET half-bridges, protected power switching, USB data, controls, and motor connectors.
- Version
- 0.3.82
- License
- unset
- Stars
- 0
scripts/pcb-audit/check-pad-anchors.py
"""Check every PCB routing terminal lies on copper belonging to that terminal."""
import json, math, sys, hashlib
from pathlib import Path
import geometry as R
path=Path(sys.argv[1]); raw=path.read_bytes(); circuit=json.loads(raw)
of=lambda kind:[x for x in circuit if x['type']==kind]
components={x['source_component_id']:x['name'] for x in of('source_component')}
sources={x['source_port_id']:x for x in of('source_port')}
pads=of('pcb_smtpad')+of('pcb_plated_hole')
def contains(pad,x,y):
if pad['shape']=='polygon':
vertices=[(p['x'],p['y']) for p in pad['points']]; inside=False
for (ax,ay),(bx,by) in zip(vertices,vertices[1:]+vertices[:1]):
dx,dy=bx-ax,by-ay; norm=dx*dx+dy*dy
t=max(0,min(1,((x-ax)*dx+(y-ay)*dy)/norm)) if norm else 0
if math.hypot(x-ax-t*dx,y-ay-t*dy)<1e-7:return True
if (ay>y)!=(by>y) and x<(bx-ax)*(y-ay)/(by-ay)+ax:inside=not inside
return inside
dx,dy=x-pad['x'],y-pad['y']
if pad['shape']=='circle':return math.hypot(dx,dy)<=pad.get('outer_diameter',2*pad.get('radius',0))/2+1e-7
if pad['shape']=='pill':
angle=math.radians(-pad.get('ccw_rotation',0));dx,dy=dx*math.cos(angle)-dy*math.sin(angle),dx*math.sin(angle)+dy*math.cos(angle)
w,h=pad.get('outer_width',pad.get('width')),pad.get('outer_height',pad.get('height')); r=min(w,h)/2
return math.hypot(max(0,abs(dx)-max(0,w-h)/2),max(0,abs(dy)-max(0,h-w)/2))<=r+1e-7
angle=math.radians(-pad.get('rect_ccw_rotation',pad.get('ccw_rotation',0)));dx,dy=dx*math.cos(angle)-dy*math.sin(angle),dx*math.sin(angle)+dy*math.cos(angle)
return abs(dx)<=pad.get('rect_pad_width',pad.get('outer_width',pad.get('width')))/2+1e-7 and abs(dy)<=pad.get('rect_pad_height',pad.get('outer_height',pad.get('height')))/2+1e-7
failures=[];checked=0
for port in of('pcb_port'):
matched=[p for p in pads if p.get('pcb_port_id')==port['pcb_port_id']]
if not matched:continue
checked+=1
if not any(contains(p,port['x'],port['y']) for p in matched):
source=sources[port['source_port_id']]
failures.append({'terminal':components.get(source['source_component_id'],'?')+'.'+str(source.get('pin_number',source.get('name'))),'x':port['x'],'y':port['y']})
report={'input':str(path),'sha256':hashlib.sha256(raw).hexdigest(),'checked':checked,'status':'FAIL' if failures else 'PASS','failures':failures}
(R.BASE/'pad-anchor-audit.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps(report,indent=2));sys.exit(bool(failures))