astra/am3352-sbc
AM3352 single-board computer with DDR3, PMIC/USB‑C 5 V power, microSD, dual USB host, HDMI video, RGB indicators, and a PWM-driven buzzer.
- Version
- 0.1.18
- License
- unset
- Stars
- 0
scripts/find-core-inner-bridge.py
"""Search existing through-vias for an F6 supply bridge without new drills.
Requires 0.30 mm edge clearance from DDR wires and 0.103 mm elsewhere.
Discovery only: does not change board source.
"""
from pathlib import Path
exec(compile((Path(__file__).parent/'route-power-distribution.py').read_text().split("print('geometry loaded'")[0],str(Path(__file__).parent/'route-power-distribution.py'),'exec'))
from scipy.ndimage import label
core=next(k for k,v in labels.items() if v=='VDD_CORE')
start=next(t for t in terminals if t['selector']=='.PG_VIA_148 > .top')
vias=[e for e in c if e['type']=='pcb_via' and net(e)==core and math.hypot(e['x']-start['x'],e['y']-start['y'])>.01]
protected_nets={net(e) for e in c if e['type']=='pcb_trace' and e['pcb_trace_id'].startswith('saved_phase_')}
width=.15;step=.05;xs=np.arange(-12,12+step/2,step);ys=np.arange(-16,8+step/2,step);W=len(xs);H=len(ys)
results=[]
for layer in ['inner1','inner2']:
foreign=[g.buffer(width/2+(.30 if n in protected_nets else .103),quad_segs=24) for g,ls,n in geoms if layer in ls and n!=core]
obs=unary_union(foreign);prepare(obs);mask=contains_xy(obs,xs[None,:],ys[:,None])
def xy(k):return (float(xs[k%W]),float(ys[k//W]))
def clear(p,q):return not obs.intersects(LineString([p,q]))
def anchors(p):
x=round((p[0]-xs[0])/step);y=round((p[1]-ys[0])/step);r=[]
for dy in range(-2,3):
for dx in range(-2,3):
xx=x+dx;yy=y+dy
if 0<=xx<W and 0<=yy<H and not mask[yy,xx] and clear(p,xy(yy*W+xx)):r.append(yy*W+xx)
return r
a=(start['x'],start['y']);starts=anchors(a);ends={}
for via in vias:
for k in anchors((via['x'],via['y'])):ends[k]=via
print(layer,'anchors',len(starts),'goals',len(ends),flush=True)
queue=[];cost={};parent={};seen=set()
for k in starts:cost[k]=math.dist(a,xy(k));heapq.heappush(queue,(cost[k],k))
found=None
while queue:
g,k=heapq.heappop(queue)
if k in seen:continue
seen.add(k)
if k in ends:found=k;break
if g>20:continue
x=k%W;y=k//W
for dx,dy in [(1,0),(-1,0),(0,1),(0,-1),(1,1),(-1,1),(1,-1),(-1,-1)]:
xx=x+dx;yy=y+dy;q=yy*W+xx
if not(0<=xx<W and 0<=yy<H) or mask[yy,xx] or q in seen:continue
ng=g+step*math.hypot(dx,dy)
if ng>=cost.get(q,math.inf) or not clear(xy(k),xy(q)):continue
cost[q]=ng;parent[q]=k;heapq.heappush(queue,(ng,q))
if found is None:results.append({'layer':layer,'found':False,'visited':len(seen)});continue
path=[found]
while path[-1] in parent:path.append(parent[path[-1]])
via=ends[found];points=[a]+[xy(k) for k in reversed(path)]+[(via['x'],via['y'])]
simple=[points[0]];i=0
while i<len(points)-1:
j=len(points)-1
while j>i+1 and not clear(points[i],points[j]):j-=1
simple.append(points[j]);i=j
results.append({'layer':layer,'found':True,'width':width,'points':simple,'targetViaId':via['pcb_via_id'],'startPortId':start['port_id'],'length':sum(math.dist(a,b) for a,b in zip(simple,simple[1:]))})
(ROOT/'output/core-inner-bridge-search.json').write_text(json.dumps(results,indent=2)+'\n')
print(json.dumps(results))