seveibar/am3352-ram-dogbone-and-single-layer-route-test

AM3352BZCZ100 processor connected to a W631GG6MB-12 DDR3 memory chip through a routed, length-matched 16-bit DDR interface with clock/data-strobe pairs and control/address signals.

Version
0.0.9
License
unset
Stars
0

scripts/audit-route-turns.py

"""Count ordinary octilinear turns without counting tessellated tuning curves."""
import json,math,sys,hashlib
from pathlib import Path

def score(points):
    ps=[]
    for p in points:
        if not ps or math.dist(p,ps[-1])>1e-8:ps.append(p)
    headings=[math.degrees(math.atan2(b[1]-a[1],b[0]-a[0]))for a,b in zip(ps,ps[1:])]
    aligned=[abs((h+22.5)%45-22.5)<.2 for h in headings]
    ordinary={i+1 for i,(a,b)in enumerate(zip(headings,headings[1:]))if aligned[i] and aligned[i+1] and abs((b-a+180)%360-180)>.2}
    return dict(ordinaryTurns=len(ordinary),shortJogSegments=sum(i in ordinary and i+1 in ordinary and math.dist(a,b)<.25 for i,(a,b)in enumerate(zip(ps,ps[1:]))))

def audit(c):
    rows=[]
    for t in c:
        if t['type']!='pcb_trace':continue
        p=[(p['x'],p['y'])for p in t['route']if p['route_type']=='wire'and p.get('layer')!='top']
        rows.append(dict(sourceTraceId=t['source_trace_id'],**score(p)))
    return dict(ordinaryTurns=sum(r['ordinaryTurns']for r in rows),shortJogSegments=sum(r['shortJogSegments']for r in rows),shortJogThresholdMm=.25,method='Count vertices between two octilinear segments (0.2 degree alignment tolerance), excluding collinear points. Curved meander tessellation is excluded. A short jog joins two counted turns and is under 0.25 mm.',routes=rows)
if __name__=='__main__':
    p=Path(sys.argv[1])if len(sys.argv)>1 else Path(__file__).resolve().parents[1]/'dist/index/circuit.json';raw=p.read_bytes();r=audit(json.loads(raw));r['circuitSha256']=hashlib.sha256(raw).hexdigest();print(json.dumps(r,indent=2))