Drogheda/scripts/2026-09-25_v6_T16_mounting_holes.py
2026-09-25 19:53:56 +00:00

80 lines
3.5 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""v6 T16 — square up the 4 mounting holes and give them unique references.
Found 25-Sep: the board (140 x 100 mm, Edge.Cuts x 60.19–200.19, y 40.32–140.32) had
MH5 (65.2, 45.3) MH6 (65.2, 135.3) <- west pair, same position since v4/v5 (proven)
MH6 (195.5, 45.0) MH6 (195.5, 135.5) <- east pair off by 0.3 / 0.2 / 0.5 mm
so the pattern was 130.3 x 90.0 on the west and 90.5 on the east, and three holes shared "MH6".
Fix: keep the proven west pair and mirror it -> a clean 130.0 x 90.0 mm rectangle, every hole
~5.0 mm from its two edges, centred on the board:
MH5 (65.2, 45.3) MH6 (65.2, 135.3) MH7 (195.2, 45.3) MH8 (195.2, 135.3)
Only the footprint (at …) line and the Reference value change; the holes keep their UUIDs.
python3 T16.py hbox-watchdog-v6.kicad_pcb # dry run
python3 T16.py hbox-watchdog-v6.kicad_pcb --apply # writes .T16bak
"""
import re, sys, shutil
TARGET = [ # (current ref, current x, y) -> (new ref, x, y)
(('MH5', 65.2, 45.3), ('MH5', 65.2, 45.3)),
(('MH6', 65.2, 135.3), ('MH6', 65.2, 135.3)),
(('MH6', 195.5, 45.0), ('MH7', 195.2, 45.3)),
(('MH6', 195.5, 135.5), ('MH8', 195.2, 135.3)),
]
def span(s, i):
d = 0; instr = False; j = i
while True:
c = s[j]
if c == '"' and s[j-1] != '\\': instr = not instr
elif not instr:
if c == '(': d += 1
elif c == ')':
d -= 1
if d == 0: return j + 1
j += 1
def holes(s):
out = []
for m in re.finditer(r'\n\t\(footprint "MountingHole:[^"]*"', s):
a = m.start() + 1; e = span(s, a); b = s[a:e]
at = re.search(r'\n\t\t\(at (-?[\d.]+) (-?[\d.]+)([^)]*)\)', b)
ref = re.search(r'\(property "Reference" "([^"]*)"', b).group(1)
out.append(dict(a=a, e=e, ref=ref, x=float(at.group(1)), y=float(at.group(2)), rot=at.group(3)))
return out
def main():
path = sys.argv[1]; apply = '--apply' in sys.argv
s0 = open(path).read(); s = s0; ch = []
hs = holes(s0)
assert len(hs) == 4, f'expected 4 mounting holes, found {len(hs)}'
edits = []
for (r0, x0, y0), (r1, x1, y1) in TARGET:
h = [h for h in hs if abs(h['x'] - x1) < 1e-3 and abs(h['y'] - y1) < 1e-3 and h['ref'] == r1]
if h: continue # already in place
h = [h for h in hs if abs(h['x'] - x0) < 1e-3 and abs(h['y'] - y0) < 1e-3 and h['ref'] == r0]
if len(h) != 1: raise SystemExit(f'cannot identify hole {r0} at ({x0},{y0})')
edits.append((h[0], r1, x1, y1))
for h, r1, x1, y1 in sorted(edits, key=lambda t: -t[0]['a']):
b = s[h['a']:h['e']]
b = re.sub(r'\n\t\t\(at (-?[\d.]+) (-?[\d.]+)([^)]*)\)', f'\n\t\t(at {x1:g} {y1:g}{h["rot"]})', b, count=1)
b = b.replace(f'(property "Reference" "{h["ref"]}"', f'(property "Reference" "{r1}"', 1)
s = s[:h['a']] + b + s[h['e']:]
ch.append(f'{h["ref"]} ({h["x"]:g},{h["y"]:g}) -> {r1} ({x1:g},{y1:g})')
hs1 = holes(s)
refs = [h['ref'] for h in hs1]
assert sorted(refs) == ['MH5', 'MH6', 'MH7', 'MH8'], refs
xs = sorted({h['x'] for h in hs1}); ys = sorted({h['y'] for h in hs1})
assert len(xs) == 2 and len(ys) == 2, 'holes not on a rectangle'
print('\n'.join(ch) or 'nothing to do (already applied)')
print(f'pattern {xs[1]-xs[0]:.2f} x {ys[1]-ys[0]:.2f} mm')
if ch and apply:
shutil.copy2(path, path + '.T16bak'); open(path, 'w').write(s); print('APPLIED (backup .T16bak)')
elif ch:
print('DRY RUN — re-run with --apply')
if __name__ == '__main__':
main()