hbox-docs/scripts/tarjetas/leer_pcb.py

64 lines
3.2 KiB
Python

"""Lee un .kicad_pcb (KiCad 8/9/10) sin KiCad y guarda en JSON lo que hace falta para dibujarlo:
pistas, arcos, vías, pads (con su red y su capa), contorno y zonas rellenadas.
Uso: python3 leer_pcb.py tarjeta.kicad_pcb pcb_v56.json"""
import re, json, math, sys
src = open(sys.argv[1], encoding='utf-8').read()
tok_re = re.compile(r'\(|\)|"(?:[^"\\]|\\.)*"|[^\s()"]+')
def parse(s):
stack=[[]]
for m in tok_re.finditer(s):
t=m.group(0)
if t=='(':
stack.append([])
elif t==')':
x=stack.pop(); stack[-1].append(x)
else:
if t.startswith('"'): t=t[1:-1]
stack[-1].append(t)
return stack[0][0]
root=parse(src)
def find(node, key):
return [c for c in node if isinstance(c,list) and c and c[0]==key]
def one(node,key):
r=find(node,key); return r[0] if r else None
out={'segments':[], 'vias':[], 'pads':[], 'edges':[], 'zones':[], 'arcs':[]}
for seg in find(root,'segment'):
s=one(seg,'start'); e=one(seg,'end'); w=one(seg,'width'); l=one(seg,'layer'); n=one(seg,'net')
out['segments'].append([float(s[1]),float(s[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else ''])
for a in find(root,'arc'):
s=one(a,'start'); m=one(a,'mid'); e=one(a,'end'); w=one(a,'width'); l=one(a,'layer'); n=one(a,'net')
out['arcs'].append([float(s[1]),float(s[2]),float(m[1]),float(m[2]),float(e[1]),float(e[2]),float(w[1]),l[1],n[1] if n else ''])
for v in find(root,'via'):
at=one(v,'at'); sz=one(v,'size'); n=one(v,'net')
out['vias'].append([float(at[1]),float(at[2]),float(sz[1]),n[1] if n else ''])
for fp in find(root,'footprint'):
at=one(fp,'at'); fx,fy=float(at[1]),float(at[2]); frot=float(at[3]) if len(at)>3 else 0.0
layer=one(fp,'layer')[1]
ref=''
for p in find(fp,'property'):
if len(p)>2 and p[1]=='Reference': ref=p[2]
for pad in find(fp,'pad'):
pat=one(pad,'at'); px,py=float(pat[1]),float(pat[2]); prot=float(pat[3]) if len(pat)>3 else 0.0
size=one(pad,'size'); w,h=float(size[1]),float(size[2])
shape=pad[3] if len(pad)>3 else 'rect'
typ=pad[2] if len(pad)>2 else 'smd'
n=one(pad,'net'); net=n[1] if n and len(n)>1 else ''
# KiCad: pad position rotated by footprint rotation (clockwise in screen coords since y down)
r=math.radians(-frot)
ax=fx+px*math.cos(r)-py*math.sin(r); ay=fy+px*math.sin(r)+py*math.cos(r)
lay=one(pad,'layers'); lays=lay[1:] if lay else []
out['pads'].append([round(ax,3),round(ay,3),w,h,prot,shape,typ,net,ref,layer,lays])
for g in root:
if isinstance(g,list) and g and g[0] in ('gr_line','gr_rect','gr_arc','gr_circle','gr_poly'):
l=one(g,'layer')
if l and l[1]=='Edge.Cuts':
out['edges'].append(g)
for z in find(root,'zone'):
n=one(z,'net'); net=n[1] if n and len(n)>1 else ''
for fpoly in find(z,'filled_polygon'):
l=one(fpoly,'layer'); pts=one(fpoly,'pts')
xy=[(float(p[1]),float(p[2])) for p in pts[1:] if isinstance(p,list) and p[0]=='xy']
out['zones'].append([l[1] if l else '', net, xy])
json.dump(out, open(sys.argv[2] if len(sys.argv) > 2 else 'pcb.json', 'w'))
print({k: len(v) for k, v in out.items()})