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/check-review-protected-copper.py

"""Prove non-reworked nets retain their explicit metal to SES export precision."""
from route_geometry import *
from shapely.ops import unary_union
from collections import defaultdict
import hashlib
baseline=Path("../../checkpoints/nema17-before-rishabh-review-v053-DuFOGi/restore-verified/dist/index/circuit.json")
before=load_circuit(str(baseline));after=load_circuit("dist/index/circuit.json");root=connectivity(after)
m=json.loads(Path("routing/net-map.json").read_text())["roots"]
moves=set(json.loads(Path("review-placement.json").read_text()))
src={e["source_component_id"]:e["name"] for e in after if e["type"]=="source_component"}
affected={root(e["source_port_id"]) for e in after if e["type"]=="source_port" and src[e["source_component_id"]] in moves}
prep=json.loads(Path("reports/swap-preparation.json").read_text())
affected|={r for r,n in m.items() if n in {e["net"] for key in ['authoredWires','authoredVias','removedWires','removedVias'] for e in prep[key]}}
islands=json.loads(Path('reports/bulk-top-physical-islands.json').read_text())
affected|={r for r,n in m.items() if n in {e['net'] for e in islands['removedForManualEscapes']}}
def copper(c):
    s={e["source_trace_id"]:e for e in c if e["type"]=="source_trace"}
    trace={e["pcb_trace_id"]:e for e in c if e["type"]=="pcb_trace"};metal=defaultdict(list)
    def net(e):
        t=s[e.get("source_trace_id",trace.get(e.get("pcb_trace_id"),{}).get("source_trace_id"))]
        return root((t.get("connected_source_port_ids",[])+t.get("connected_source_net_ids",[]))[0])
    for e in c:
        if e["type"]=="pcb_trace" and e.get("source_trace_id"):
            n=net(e)
            for a,b in zip(e["route"],e["route"][1:]):
                if a["route_type"]==b["route_type"]=="wire" and a["layer"]==b["layer"] and math.dist((a["x"],a["y"]),(b["x"],b["y"]))>1e-8:
                    metal[n,a["layer"]].append(LineString([(a["x"],a["y"]),(b["x"],b["y"])]).buffer(max(a["width"],b["width"])/2,quad_segs=16))
        elif e["type"]=="pcb_via":
            for l in LAYERS:metal[net(e),l].append(Point(e["x"],e["y"]).buffer(e["outer_diameter"]/2,quad_segs=16))
    return {k:unary_union(v) for k,v in metal.items() if k[0] not in affected}
a,b=copper(before),copper(after);assert a.keys()==b.keys()
changed=[]
for k,g in a.items():
    if not g.buffer(.0002).covers(b[k]) or not b[k].buffer(.0002).covers(g):changed.append(dict(net=m.get(k[0],k[0]),layer=k[1],areaDeltaMm2=g.symmetric_difference(b[k]).area))
report=dict(circuitSha256=hashlib.sha256(Path("dist/index/circuit.json").read_bytes()).hexdigest(),protectedNetCount=len({k[0] for k in a}),
            protectedNetLayers=len(a),comparisonToleranceMm=.0002,unintendedChanges=changed,
            reworkedNets=sorted({m.get(r,r) for r in affected}))
Path("reports/review-protected-copper.json").write_text(json.dumps(report,indent=2)+"\n")
print(json.dumps(report));assert not changed