mohan-bee/gimbal-one-budget-pcb
A 4-layer STM32-based gimbal controller PCB with an onboard LSM6DS3TR-C IMU, three DRV8313 3-phase motor drivers, protected 3S battery input, 3.3 V buck regulation, and SWD/UART control interfaces.
- Version
- 0.1.0
- License
- unset
- Stars
- 0
verify-power.ts
PCB
Schematic
import assert from "node:assert/strict"
import {readFileSync,writeFileSync} from "node:fs"
const circuit=JSON.parse(readFileSync("dist/controller/circuit.json","utf8"))
const nets=new Map(circuit.filter((x:any)=>x.type==="source_net").map((x:any)=>[x.source_net_id,x.name]))
const traces=JSON.parse(readFileSync("routing/checked-traces.json","utf8"))
const report:any[]=[]
for(const net of ["BAT_POS","BAT_FUSED","VM",...['PITCH','ROLL','YAW'].flatMap(a=>['U','V','W'].map(p=>`${a}_${p}`))]){
const selected=traces.filter((t:any)=>nets.get(t.connection_name)===net)
assert.ok(selected.length,`Missing ${net}`)
let length=0,resistance=0,narrowLength=0,minWidth=Infinity,maxWidth=0
for(const t of selected)for(let i=1;i<t.route.length;i++){
const a=t.route[i-1],b=t.route[i]
if(a.route_type!=="wire"||b.route_type!=="wire"||a.layer!==b.layer)continue
const width=Math.min(a.width,b.width),l=Math.hypot(a.x-b.x,a.y-b.y)
assert.ok(width>=.2-1e-6,`${net}: power copper narrower than 0.20 mm`)
length+=l;resistance+=1.724e-5*l/(width*.035);if(width<.4)narrowLength+=l
minWidth=Math.min(minWidth,width);maxWidth=Math.max(maxWidth,width)
}
const phase=/_(U|V|W)$/.test(net)
assert.ok(maxWidth>=(phase ? .5 : 1)-1e-6,`${net} main width missing`)
report.push({net,traceCount:selected.length,lengthMm:length,minWidthMm:minWidth,maxWidthMm:maxWidth,lengthBelow04Mm:narrowLength,sumOfSegmentResistancesOhmAt20C1oz:resistance})
}
assert.deepEqual([...new Set(circuit.filter((x:any)=>x.type==="pcb_copper_pour").map((x:any)=>x.layer))].sort(),["bottom","inner1","top"])
assert.equal(circuit.filter((x:any)=>x.type.endsWith('_error')).length,0)
for(const v of circuit.filter((x:any)=>x.type==="pcb_via"))assert.ok((v.outer_diameter-v.hole_diameter)/2>=.1-1e-6)
const orientations=JSON.parse(readFileSync("assembly-orientation-review.json","utf8"))
assert.equal(orientations.length,74)
assert.equal(orientations.filter((x:any)=>x.method==="post-reflow manual assembly").length,9)
assert.equal(orientations.find((x:any)=>x.designator==="D_GS").placementRotation,270)
assert.equal(orientations.find((x:any)=>x.designator==="D_TVS").placementRotation,90)
assert.deepEqual(JSON.parse(readFileSync("placement-rotation-warnings.json","utf8")),[])
writeFileSync("power-width-validation.json",JSON.stringify({status:"geometry_pass",copperAssumption:"35 um minimum finished copper on all four layers; resistance excludes vias and contacts, not a thermal rating",nets:report},null,2))
console.log("PASS: power widths, three ground layers, via annular rings and all 74 placement dispositions")