shibosoftwaredev/f1c100s-linux-nema17-stepper-controller
A four-layer NEMA 17 motor-controller PCB combining an Allwinner F1C100S Linux computer, USB‑PD/buck power system, W5500 Ethernet, STM32 real-time motion control, DRV8825 stepper driver, protection/temperature/current telemetry, USB interfaces, and local LED/buzzer diagnostics.
- Version
- 0.4.56
- License
- unset
- Stars
- 0
scripts/find-swap-pd-resistor.py
"""Search one local PD resistor placement and its actual top-side links."""
from route_geometry import *
from shapely.ops import nearest_points,unary_union
from shapely.strtree import STRtree
c=load_circuit('routing/review-placement.circuit.json');root=connectivity(c)
m=json.loads(Path('routing/net-map.json').read_text())['roots'];ports={e['pcb_port_id']:e for e in c if e['type']=='pcb_port'}
names={e['source_component_id']:e['name'] for e in c if e['type']=='source_component'};pc={e['pcb_component_id']:names[e['source_component_id']] for e in c if e['type']=='pcb_component'}
p=json.loads(Path('routing/swap-last-open-physical.json').read_text())
courts=[];pads=[]
for e in c:
if pc.get(e.get('pcb_component_id'))=='R_PD_12V':continue
if e['type']=='pcb_courtyard_rect' and e['layer']=='top':courts.append(geometry({**e,**e['center'],'shape':'rect'}))
if e['type']=='pcb_courtyard_outline' and e['layer']=='top' and len(e['outline'])>=3:courts.append(Polygon([(q['x'],q['y']) for q in e['outline']]).buffer(0))
if e['type'] in ['pcb_smtpad','pcb_plated_hole']:
q=ports.get(e.get('pcb_port_id'));n=m[root(q['source_port_id'])] if q else 'NC'
if 'top' in e.get('layers',[e.get('layer')]):pads.append((n,geometry(e,'outer_' if e['type']=='pcb_plated_hole' else '')))
ct=STRtree(courts);pt=STRtree([g for n,g in pads]);vs=[Point(v['x'],v['y']) for v in p['vias']];vt=STRtree(vs)
metal=[(w['net'],w['width'],LineString(w['points']).buffer(w['width']/2),LineString(w['points'])) for w in p['wires'] if w['layer']=='top'];mt=STRtree([g for n,w,g,l in metal])
ground=unary_union([l for n,w,g,l in metal if n=='N62'])
protected={'N57','N68','N99','N101','N163','N165','N166','N179','N209'}
results=[]
for rot in [0,90,180,270]:
for ix in range(120,201):
for iy in range(-10,101):
x,y=ix/10,iy/10
court=translate(rotate(box(-.93,-.47,.93,.47),rot,origin=(0,0)),x,y)
if any(court.intersects(courts[int(i)]) for i in ct.query(court)):continue
pp=[translate(rotate(box(dx-.27,-.32,dx+.27,.32),rot,origin=(0,0)),x,y) for dx in [-.51,.51]]
xy=[(g.centroid.x,g.centroid.y) for g in pp]
lines=[LineString([xy[0],(16,3.59)]),LineString([xy[1],nearest_points(Point(xy[1]),ground)[1]])]
if pp[0].distance(lines[1])<.1755 or pp[1].distance(lines[0])<.1755 or lines[0].distance(lines[1])<.2505:continue
bad=False;cuts=set()
for n,g,line in zip(['N93','N62'],pp,lines):
if any(g.distance(vs[int(i)])<.354 or line.distance(vs[int(i)])<.4295 and p['vias'][int(i)]['net']!=n for i in set(map(int,vt.query(g.buffer(.354))))|set(map(int,vt.query(line.buffer(.4295))))):bad=True;break
if any(nn!=n and (h.distance(g)<.1005 or h.distance(line)<.1755) for nn,h in (pads[int(i)] for i in set(map(int,pt.query(g.buffer(.101))))|set(map(int,pt.query(line.buffer(.176)))))):bad=True;break
for i in set(map(int,mt.query(g.buffer(.101))))|set(map(int,mt.query(line.buffer(.176)))):
nn,w,h,l=metal[i]
if nn==n or min(h.distance(g)-.1005,h.distance(line)-.1755)>=0:continue
if w>=.299 or nn in protected:bad=True;break
cuts.add(i)
if bad:break
if not bad:results.append(dict(score=round(sum(l.length for l in lines)+len(cuts)*.8,4),x=x,y=y,rotation=rot,links=[list(l.coords) for l in lines],cuts=[dict(net=metal[i][0],width=metal[i][1]) for i in cuts]))
results.sort(key=lambda x:x['score'])
Path('reports/swap-pd-resistor-options.json').write_text(json.dumps(results[:30],indent=2)+'\n')
print(json.dumps(results[:8]))