hrithik18k/punck-tscircuit-recreation

A four-layer Eurorack drum/sampler controller PCB built around an STM32H743, dual QSPI flash, PCM5100 stereo DAC, TL074 analog audio circuitry, input clamps, 12V-to-3.3V power regulation, MIDI/USB/SWD connectors, controls, LEDs, and trigger/audio I/O.

Version
1.0.3
License
unset
Stars
0

scripts/audit.ts

import fs from 'node:fs'
import {parse, children, child, atom} from './sexpr'
const report: any[] = []
for (const name of ['Components','Controls','Panel']) {
  const src = parse(fs.readFileSync(`source/Punck_${name}.kicad_pcb`, 'utf8'))
  const out = parse(fs.readFileSync(`dist/Punck_${name}.kicad_pcb`, 'utf8'))
  const cj = JSON.parse(fs.readFileSync(`boards/${name.toLowerCase()}.pcb.circuit.json`, 'utf8'))
  const combined = JSON.parse(fs.readFileSync(`boards/${name.toLowerCase()}.combined.circuit.json`, 'utf8'))
  const count = (tree:any) => Object.fromEntries(['footprint','segment','via','zone','gr_line','gr_arc','gr_circle','gr_rect','gr_poly','gr_text'].map(t=>[t,children(tree,t).length]))
  const fps = children(src,'footprint')
  const refs = fps.map(f=>atom(children(f,'property').find(p=>atom(p[1])==='Reference')?.[2]))
  const duplicates = refs.filter(r=>combined.filter((e:any)=>e.type==='source_component'&&e.name===r).length>1)
  const zeroSize = cj.filter((e:any)=>e.type==='pcb_component' && (!e.width || !e.height)).length
  const ports = new Set(cj.filter((e:any)=>e.type==='source_port').map((e:any)=>e.source_port_id))
  const danglingPorts = cj.filter((e:any)=>e.type==='pcb_port'&&!ports.has(e.source_port_id))
  const padCounts = Object.fromEntries(['smd','thru_hole','np_thru_hole'].map(t=>[t,fps.flatMap(f=>children(f,'pad')).filter(p=>p[2]===t).length]))
  const segments = children(src,'segment')
  const sourceLength = segments.reduce((acc,s)=>{const a=child(s,'start')!,b=child(s,'end')!; return acc+Math.hypot(Number(a[1])-Number(b[1]),Number(a[2])-Number(b[2]))},0)
  let cjLength = 0, wireEdges = 0
  for (const t of cj.filter((e:any)=>e.type==='pcb_trace')) 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){cjLength+=Math.hypot(a.x-b.x,a.y-b.y);wireEdges++}}
  report.push({name, source:count(src), roundtrip:count(out), padCounts, board:cj.find((e:any)=>e.type==='pcb_board'), duplicates, zeroSize, danglingPorts, sourceLength,cjLength,wireEdges,sourceNets:children(src,'net').length, cjNets:cj.filter((e:any)=>e.type==='source_net').length})
}
fs.writeFileSync('evidence/fidelity-audit.json',JSON.stringify(report,null,2))
console.log(JSON.stringify(report.map(({board,...r})=>({...r,board:{width:board.width,height:board.height,num_layers:board.num_layers}})),null,2))