Heatsinks, wider 12 V tracks, design review; pause project

- PCB rev 0.1: Power3A class (1.5 mm) for the 12 V input path, Aavid
  clip-on heatsinks on Q3/Q5/Q6 (project footprint library), T-junction
  healing and via shrink in the router; DRC 0 errors
- Current-capacity and heatsink documentation
- CLAUDE.md with project decisions, workflow and gotchas
- docs/design-review.md: review of a module-only PSU (rev 0.2) and cost
  estimate
- Project paused for budget and safety reasons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mangavalkandClaude Opus 5.5 committed 2026-10-24 15:07:58 +02:00
1 parent 28c666506e
commit cdf24e0b4b
15 files changed
+5917 -4730

No files matched your search

+53 -2
View File
@@ -26,7 +26,8 @@ PASSES = int(sys.argv[1]) if len(sys.argv) > 1 else 100
# (routing clearance + half the width difference) from its neighbours. This gets 3 mm HV <-> LV
# and 1.3 mm HV <-> HV while tracks can still squeeze out between transistor pins.
# class: (routing width um, routing clearance um, final width mm)
ROUTE = {"kicad_default": (1800, 250, 0.4), "Power": (2400, 300, 1.0), "HV": (2100, 650, 0.6)}
ROUTE = {"kicad_default": (1800, 250, 0.4), "Power": (2400, 300, 1.0), "Power3A": (2900, 300, 1.5),
"HV": (2100, 650, 0.6)}
VIA_ROUTE_UM, VIA_FINAL_MM, VIA_DRILL_MM = 2300, 1.0, 0.5 # same trick for vias
HV_TO_LV_UM = 2450 # + (2.1 - 0.6) / 2 after shrinking = 3.2 mm
@@ -62,7 +63,7 @@ def add_connector_stubs(board, ref="J2", length=5.0):
"""
fp = board.FindFootprintByReference(ref)
# final widths: a router track must end on the stub copper itself, which still holds after shrinking
widths = {"HV": ROUTE["HV"][2], "Power": ROUTE["Power"][2]}
widths = {k: v[2] for k, v in ROUTE.items()}
for pad in fp.Pads():
net = pad.GetNet()
name = net.GetNetname() if net else ""
@@ -81,6 +82,55 @@ def add_connector_stubs(board, ref="J2", length=5.0):
board.Add(t)
def _closest_on_segment(p, a, b):
ax, ay, bx, by = a.x, a.y, b.x, b.y
dx, dy = bx - ax, by - ay
L2 = dx * dx + dy * dy
u = 0.0 if L2 == 0 else max(0.0, min(1.0, ((p.x - ax) * dx + (p.y - ay) * dy) / L2))
cx, cy = ax + u * dx, ay + u * dy
return int(round(cx)), int(round(cy)), ((p.x - cx) ** 2 + (p.y - cy) ** 2) ** 0.5
def heal_t_junctions(board, reach_mm=1.6):
"""After shrinking, a track that joined another track mid-way (a T-junction) can end beside
the narrowed copper. Snap such dangling ends onto the centre line of the same-net track."""
traces = [t for t in board.GetTracks() if t.Type() == pcbnew.PCB_TRACE_T]
vias = [t for t in board.GetTracks() if t.Type() == pcbnew.PCB_VIA_T]
pads = [p for f in board.GetFootprints() for p in f.Pads()]
reach = pcbnew.FromMM(reach_mm)
fixed = 0
for t in traces:
for is_start in (True, False):
p = t.GetStart() if is_start else t.GetEnd()
net, layer = t.GetNetCode(), t.GetLayer()
if any(pd.GetNetCode() == net and pd.IsOnLayer(layer) and pd.HitTest(p) for pd in pads):
continue
if any(v.GetNetCode() == net and v.GetPosition() == p for v in vias):
continue
best, touching = None, False
for o in traces:
if o is t or o.GetNetCode() != net or o.GetLayer() != layer:
continue
if p in (o.GetStart(), o.GetEnd()):
touching = True
break
cx, cy, d = _closest_on_segment(p, o.GetStart(), o.GetEnd())
if d <= o.GetWidth() / 2:
touching = True
break
if d <= reach and (best is None or d < best[2]):
best = (cx, cy, d)
if touching or best is None:
continue
q = pcbnew.VECTOR2I(best[0], best[1])
if is_start:
t.SetStart(q)
else:
t.SetEnd(q)
fixed += 1
return fixed
def unconnected(board):
board.BuildConnectivity()
return board.GetConnectivity().GetUnconnectedCount(False)
@@ -199,6 +249,7 @@ def main():
if not pcbnew.ImportSpecctraSES(board, str(ses)):
raise SystemExit("SES import failed")
shrink_tracks(board)
print("healed T-junctions:", heal_t_junctions(board))
remove_dangling_stubs(board)
add_gnd_plane(board)
pcbnew.ZONE_FILLER(board).Fill(board.Zones())