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-fab-buck-pocket.py
"""Preflight two proposed component sites without installing them."""
from route_geometry import *
from shapely.strtree import STRtree
c = load_circuit("routing/fab-buck-pocket.circuit.json")
root = connectivity(c)
sources = {e["source_component_id"]: e for e in c if e["type"] == "source_component"}
pcbs = {e["pcb_component_id"]: e for e in c if e["type"] == "pcb_component"}
ports = {e["pcb_port_id"]: e for e in c if e["type"] == "pcb_port"}
traces = {e["source_trace_id"]: e for e in c if e["type"] == "source_trace"}
affected = {sid for sid, trace in traces.items() if trace.get("name") in
{"BUCK_FB_TOP", "BUCK_FF_CAP", "BUCK_FF_RETURN"}}
moved_names = {"R_BUCK_FF", "C_BUCK_FF"}
moved_ids = {p["pcb_component_id"] for p in pcbs.values()
if sources[p["source_component_id"]]["name"] in moved_names}
objects = []
def add(net, layers, shape, label):
objects.append((net, set(layers), shape, label))
for e in c:
if e["type"] == "pcb_hole":
add("NPTH", LAYERS, geometry(e, "hole_").buffer(.1001), "NPTH")
if e["type"] not in ["pcb_smtpad", "pcb_plated_hole"]: continue
port = ports.get(e.get("pcb_port_id"))
net = root(port["source_port_id"]) if port else "NC_" + e.get("pcb_port_id", str(e))
add(net, e.get("layers", [e.get("layer")]), geometry(e, "outer_" if e["type"] == "pcb_plated_hole" else ""),
"pad:" + sources[pcbs[e["pcb_component_id"]]["source_component_id"]]["name"])
for t in json.loads(Path("routing/complete-routes.json").read_text()):
if t.get("source_trace_id") in affected: continue
source = traces.get(t.get("source_trace_id"))
ids = (source or {}).get("connected_source_port_ids", []) + (source or {}).get("connected_source_net_ids", [])
net = root(ids[0]) if ids else "unknown"
for a, b in zip(t["route"], t["route"][1:]):
if a["route_type"] == b["route_type"] == "wire" and a["layer"] == b["layer"]:
if math.dist((a["x"], a["y"]), (b["x"], b["y"])) < 1e-8: continue
add(net, [a["layer"]], LineString([(a["x"], a["y"]), (b["x"], b["y"])]).buffer(max(a["width"], b["width"])/2),
"route:" + (source or {}).get("name", "unknown"))
for p in t["route"]:
if p["route_type"] == "via": add(net, LAYERS, Point(p["x"], p["y"]).buffer(.225), "via")
findings = []
for pad in [e for e in c if e["type"] == "pcb_smtpad" and e["pcb_component_id"] in moved_ids]:
net = root(ports[pad["pcb_port_id"]]["source_port_id"])
shape = geometry(pad)
name = sources[pcbs[pad["pcb_component_id"]]["source_component_id"]]["name"]
for other_net, layers, other, label in objects:
if other_net == net or pad["layer"] not in layers: continue
gap = shape.distance(other)
if gap < .1-1e-6: findings.append(dict(part=name, x=pad["x"], y=pad["y"], against=label, clearance=round(gap, 6)))
for moved in [pcbs[i] for i in moved_ids]:
g = box(moved["center"]["x"]-moved["width"]/2, moved["center"]["y"]-moved["height"]/2,
moved["center"]["x"]+moved["width"]/2, moved["center"]["y"]+moved["height"]/2)
for other in pcbs.values():
if other is moved or other["layer"] != moved["layer"]: continue
h = box(other["center"]["x"]-other["width"]/2, other["center"]["y"]-other["height"]/2,
other["center"]["x"]+other["width"]/2, other["center"]["y"]+other["height"]/2)
if g.distance(h) < .15-1e-6:
findings.append(dict(part=sources[moved["source_component_id"]]["name"],
against="component:"+sources[other["source_component_id"]]["name"], clearance=round(g.distance(h), 6)))
Path("reports/fab-buck-pocket-preflight.json").write_text(json.dumps(findings, indent=2)+"\n")
print(json.dumps(dict(candidateOnly=True, findings=findings)))