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-authored-copper.py

"""Verify the imported build really includes the manually engineered conductors."""
from route_geometry import *
from shapely.ops import unary_union
from collections import defaultdict
import hashlib
c=load_circuit('dist/index/circuit.json');root=connectivity(c)
m=json.loads(Path('routing/net-map.json').read_text())['roots']
s={e['source_trace_id']:e for e in c if e['type']=='source_trace'}
t={e['pcb_trace_id']:e for e in c if e['type']=='pcb_trace'}
def net(e):
    a=s[e.get('source_trace_id',t.get(e.get('pcb_trace_id'),{}).get('source_trace_id'))]
    return m[root((a.get('connected_source_port_ids',[])+a.get('connected_source_net_ids',[]))[0])]
wires=defaultdict(list);vias=[]
for e in c:
    if e['type']=='pcb_trace' and e.get('source_trace_id'):
        for a,b in zip(e['route'],e['route'][1:]):
            if a['route_type']==b['route_type']=='wire' and a['layer']==b['layer']:
                w=max(a['width'],b['width'])
                wires[net(e),a['layer']].append((w,LineString([(a['x'],a['y']),(b['x'],b['y'])]).buffer(w/2,quad_segs=16)))
    elif e['type']=='pcb_via':vias.append((net(e),e))
plan=json.loads(Path('reports/bulk-top-preparation.json').read_text());failures=[]
patches=json.loads(Path('routing/bulk-top-island-patches.json').read_text())
plan['authoredWires']+=patches['wires'];plan['authoredVias']+=patches['vias']
ethernet=json.loads(Path('routing/review-ethernet-waypoints.json').read_text())
plan['authoredWires']+=ethernet['wires'];plan['authoredVias']+=ethernet['vias']
clock=json.loads(Path('routing/review-clock-waypoints.json').read_text())
plan['authoredWires']+=clock['wires'];plan['authoredVias']+=clock['vias']
clock_fanout=json.loads(Path('routing/review-clock-fanout.json').read_text())
plan['authoredWires']+=clock_fanout['wires'];plan['authoredVias']+=clock_fanout['vias']
finished=json.loads(Path('routing/review-finished-copper.json').read_text())
plan['authoredWires']+=finished['wires'];plan['authoredVias']+=finished['vias']
restored=json.loads(Path('routing/review-restored-status.json').read_text())
plan['authoredWires']+=restored['wires'];plan['authoredVias']+=restored['vias']
fanouts=json.loads(Path('routing/review-final-fanouts.json').read_text())
plan['authoredWires']+=fanouts['wires'];plan['authoredVias']+=fanouts['vias']
for relocation in fanouts.get('relocatedAuthoredVias',[]):
    matches=[v for v in plan['authoredVias'] if v['net']==relocation['from']['net'] and math.dist((v['x'],v['y']),(relocation['from']['x'],relocation['from']['y']))<1e-7]
    assert len(matches)==1 and relocation['to'] in fanouts['vias']
    plan['authoredVias'].remove(matches[0])
for w in plan['authoredWires']:
    metal=unary_union([g for width,g in wires[w['net'],w['layer']] if width>=w['width']-1e-6])
    expected=LineString(w['points']).buffer(w['width']/2,quad_segs=16)
    if not metal.buffer(.0002).covers(expected):failures.append(dict(kind='missing_or_narrowed_authored_copper',wire=w))
for p in plan['authoredVias']:
    if not any(n==p['net'] and math.dist((v['x'],v['y']),(p['x'],p['y']))<1e-5 and
               abs(v['hole_diameter']-.3)<1e-8 and abs(v['outer_diameter']-.45)<1e-8 and v['layers']==LAYERS for n,v in vias):
        failures.append(dict(kind='missing_authored_through_via',via=p))
report=dict(circuitSha256=hashlib.sha256(Path('dist/index/circuit.json').read_bytes()).hexdigest(),
            wiresChecked=len(plan['authoredWires']),viasChecked=len(plan['authoredVias']),failures=failures)
Path('reports/review-authored-copper.json').write_text(json.dumps(report,indent=2)+'\n')
print(json.dumps(report));assert not failures