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/preserve-round7-unchanged.py
"""Retain checked source paths only where the candidate has identical copper.
Run after a successful full physical-graph import, before the normal build.
Never supplies connectivity or waives an import failure. The final emitted
copper still requires independent continuity, clearance, and shorts checks.
"""
from route_geometry import *
from shapely.ops import unary_union
from collections import defaultdict
import hashlib
c=load_circuit("routing/placement.json");root=connectivity(c)
report=json.loads(Path("routing/import-report.json").read_text())
assert not report["failures"] and report["physical_island_links"]==0
assert report["circuit_sha256"]==hashlib.sha256(Path("routing/placement.json").read_bytes()).hexdigest()
physical=json.loads(Path("routing/physical-copper.json").read_text())
mapping=json.loads(Path("routing/net-map.json").read_text())["roots"]
sources={s["source_trace_id"]:s for s in c if s["type"]=="source_trace"}
ports={p["pcb_port_id"]:p for p in c if p["type"]=="pcb_port"}
def net(t):
s=sources[t["source_trace_id"]]
return mapping[root((s.get("connected_source_port_ids",[])+s.get("connected_source_net_ids",[]))[0])]
metal=defaultdict(list)
for w in physical["wires"]:metal[w["net"],w["layer"]].append(LineString(w["points"]).buffer(w["width"]/2,quad_segs=16))
metal={k:unary_union(gs).buffer(2e-6) for k,gs in metal.items()}
seed=defaultdict(list)
for t in json.loads(Path("routing/round7-usb-retained.json").read_text()):seed[t["source_trace_id"]].append(t)
def still_exact(t):
n=net(t)
for endpoint,key in [(t["route"][0],"start_pcb_port_id"),(t["route"][-1],"end_pcb_port_id")]:
p=ports.get(endpoint.get(key))
if not p or math.hypot(p["x"]-endpoint["x"],p["y"]-endpoint["y"])>1e-6:return False
if mapping[root(p["source_port_id"])]!=n:return False
for p in t["route"]:
if p["route_type"]=="via" and not any(v["net"]==n and math.hypot(v["x"]-p["x"],v["y"]-p["y"])<2e-6 for v in physical["vias"]):return False
for a,b in zip(t["route"],t["route"][1:]):
if a["route_type"]==b["route_type"]=="wire" and a["layer"]==b["layer"] and math.hypot(a["x"]-b["x"],a["y"]-b["y"])>1e-8:
g=LineString([(a["x"],a["y"]),(b["x"],b["y"])]).buffer(max(a["width"],b["width"])/2,quad_segs=16)
if (n,a["layer"]) not in metal or not metal[n,a["layer"]].covers(g):return False
return True
accepted={sid for sid,ts in seed.items() if all(still_exact(t) for t in ts)}
current=json.loads(Path("routing/complete-routes.json").read_text())
output=[t for t in current if t["source_trace_id"] not in accepted]+[t for sid in accepted for t in seed[sid]]
output=[{**t,"pcb_trace_id":f"whole_board_{i}"} for i,t in enumerate(output)]
Path("routing/complete-routes.json").write_text(json.dumps(output,indent=2)+"\n")
result=dict(preservedSourceIds=sorted(accepted),preservedPaths=sum(len(seed[s]) for s in accepted),paths=len(output),notYetBuildValidated=True)
Path("reports/round7-unchanged-copper-preservation.json").write_text(json.dumps(result,indent=2)+"\n")
print(json.dumps(dict(preservedSources=len(accepted),preservedPaths=result["preservedPaths"],paths=len(output))))