pixalynx/nrf9151-gps-tracker

A compact 44×32 mm, 4-layer nRF9151 LTE-M/NB-IoT/GNSS tracker PCB with nPM1300 LiPo/USB-C power, nano-SIM/eSIM switching, nRF7002 Wi‑Fi scanning, accelerometer, antennas, buzzer, LEDs, SOS button, and SWD/debug connectors.

Version
0.1.4
License
unset
Stars
0

scripts/check-holes.py

#!/usr/bin/env python3
"""Non-plated holes (connector pegs, Tag-Connect guide pins) against every pad on either side: the placement checker
does not flag a peg hole drilled through another part's land. Fails when a hole edge is closer than MIN to a pad.
Usage: python3 scripts/check-holes.py [dist/index/circuit.json]"""
import json, math, sys
MIN = 0.2
d = json.load(open(sys.argv[1] if len(sys.argv) > 1 else 'dist/index/circuit.json'))
src = {c['source_component_id']: c['name'] for c in d if c['type'] == 'source_component'}
pc = {c['pcb_component_id']: src.get(c['source_component_id']) for c in d if c['type'] == 'pcb_component'}
bad = 0
for h in (e for e in d if e['type'] == 'pcb_hole'):
    r = (h.get('hole_diameter') or max(h.get('hole_width', 0), h.get('hole_height', 0))) / 2
    for p in (e for e in d if e['type'] == 'pcb_smtpad' and e.get('pcb_component_id') != h.get('pcb_component_id')):
        w = p.get('width', 2 * p.get('radius', 0)); hh = p.get('height', 2 * p.get('radius', 0))
        if round((p.get('ccw_rotation') or 0) / 90) % 2: w, hh = hh, w
        gap = math.hypot(max(abs(p['x'] - h['x']) - w / 2, 0), max(abs(p['y'] - h['y']) - hh / 2, 0)) - r
        if gap < MIN:
            bad += 1; print(f"hole of {pc.get(h.get('pcb_component_id'))} at ({h['x']:.2f},{h['y']:.2f}) is {gap:.2f} mm from a {p['layer']} pad of {pc.get(p.get('pcb_component_id'))}")
print(f"holes: {bad} conflict(s)"); sys.exit(1 if bad else 0)