imrishabh18/nema-23-stepper-controller

A 57 mm four-layer RP2040 NEMA23 stepper-controller PCB with USB‑C/USB‑PD power input, protected motor rail, DRV8452 dual-winding driver, bidirectional current sensing, temperature/enable interlock, optional magnetic encoder, RGB LED and buzzer status indicators, and motor/test connectors.

Version
1.2.3
License
unset
Stars
0

scripts/check-power-layers.mjs

import {readFileSync,writeFileSync,mkdirSync} from 'node:fs'
import {createHash} from 'node:crypto'
import assert from 'node:assert/strict'
const path=process.argv[2]??'dist/index/circuit.json'
const json=JSON.parse(readFileSync(path)),policy=JSON.parse(readFileSync('routing/power-layer-policy.json'))
const parent=new Map()
const root=x=>{if(!parent.has(x))parent.set(x,x);if(parent.get(x)!==x)parent.set(x,root(parent.get(x)));return parent.get(x)}
for(const e of json){
 const ids=e.type==='source_trace'?[...e.connected_source_port_ids,...e.connected_source_net_ids]:e.type==='source_component_internal_connection'?e.source_port_ids:[]
 for(const id of ids.slice(1))parent.set(root(id),root(ids[0]))
}
const netRoots=new Map(json.filter(e=>e.type==='source_net').map(e=>[e.name,root(e.source_net_id)]))
for(const name of policy.netNames)assert.ok(netRoots.has(name),`Missing required rail ${name}`)
const power=new Map(policy.netNames.map(name=>[netRoots.get(name),name]))
assert.ok(!power.has(netRoots.get('GND')),'Power must remain separate from GND')
const traces=new Map(json.filter(e=>e.type==='source_trace').map(e=>[e.source_trace_id,root([...e.connected_source_port_ids,...e.connected_source_net_ids][0])]))
const checked=[],violations=[]
for(const e of json){
 const net=power.get(traces.get(e.source_trace_id)??root(e.source_net_id))
 if(!net)continue
 if(e.type==='pcb_trace'){
  const bad=e.route.filter(r=>r.route_type==='wire'?!policy.allowedLayers.includes(r.layer):r.route_type==='via'?!policy.allowedLayers.includes(r.from_layer)||!policy.allowedLayers.includes(r.to_layer):r.route_type==='through_pad'?!policy.allowedLayers.includes(r.start_layer)||!policy.allowedLayers.includes(r.end_layer):false)
  checked.push({net,traceId:e.pcb_trace_id,layers:[...new Set(e.route.filter(r=>r.route_type==='wire').map(r=>r.layer))]})
  if(bad.length)violations.push({net,traceId:e.pcb_trace_id,points:bad})
 }else if(e.type==='pcb_copper_pour'&&!policy.allowedLayers.includes(e.layer))violations.push({net,pourId:e.pcb_copper_pour_id,layer:e.layer})
}
assert.ok(checked.length>50,'Power routing audit found too few traces; build may be incomplete')
mkdirSync('checks',{recursive:true})
writeFileSync('checks/power-layers.json',JSON.stringify({circuitJsonSha256:createHash('sha256').update(readFileSync(path)).digest('hex'),policy,checkedTraceCount:checked.length,checked,violations},null,2)+'\n')
assert.equal(violations.length,0,JSON.stringify(violations,null,2))
console.log(`PASS: ${checked.length} supply, charge-pump and load traces use top/bottom only; GND may use inner layers.`)