Integrate A1 carrier PCB CAD updates

- Add pinned STEP-derived DE-9 mechanical measurements and provenance
- Refresh placement, routing, silkscreen, and nominal CAD envelopes
- Document provisional dimensions, assumptions, and validation status
This commit is contained in:
2026-09-21 12:31:24 +02:00
parent 06df47c934
commit c0f7a1ad51
13 changed files with 5825 additions and 3966 deletions
+38 -3
View File
@@ -4,6 +4,7 @@
Requires KiCad's pcbnew binding in system Python. The known U2 footprint parity
warning is reported explicitly, not hidden or added to KiCad's exclusions.
"""
import hashlib
import json
from pathlib import Path
import subprocess
@@ -29,7 +30,33 @@ assert p.ToMM(board.GetDesignSettings().GetBoardThickness())==1.6
outline=[d for d in board.GetDrawings() if d.GetLayer()==p.Edge_Cuts]
assert len(outline)==6
points={(round(p.ToMM(v.x)-100,5),round(p.ToMM(v.y)-100,5)) for d in outline for v in (d.GetStart(),d.GetEnd())}
assert points=={(0,0),(82,0),(82,80),(36,80),(36,61),(0,61)}
expected_outline=[(36,0),(82,0),(82,80),(0,80),(0,19),(36,19)]
assert points==set(expected_outline)
expected_edges={frozenset((a,b)) for a,b in zip(expected_outline,expected_outline[1:]+expected_outline[:1])}
actual_edges={frozenset((round(p.ToMM(v.x)-100,5),round(p.ToMM(v.y)-100,5))
for v in (d.GetStart(),d.GetEnd())) for d in outline}
assert actual_edges==expected_edges
placements={"U1":(8,23),"U2":(43,26.670),"DS1":(43,36),
"R1":(43,34),"R2":(54,34),"R3":(65,34),
"SW1":(43,68),"SW2":(55,68),"SW3":(67,68),
"H1":(3.5,22.5),"H2":(3.5,75),"H3":(78,4),"H4":(78,76)}
for ref,(x,y) in placements.items():
position=footprints[ref].GetPosition()
assert position.x==p.FromMM(x+100) and position.y==p.FromMM(y+100),ref
assert footprints[ref].GetOrientationDegrees()==0,ref
assert len(board.Zones())==1
reserve=board.Zones()[0]
assert reserve.GetIsRuleArea() and reserve.IsOnLayer(p.F_Cu) and reserve.IsOnLayer(p.B_Cu)
assert reserve.GetDoNotAllowTracks() and reserve.GetDoNotAllowVias() and reserve.GetDoNotAllowZoneFills()
contour=reserve.Outline().COutline(0)
assert {(p.ToMM(contour.CPoint(i).x),p.ToMM(contour.CPoint(i).y)) for i in range(contour.PointCount())}=={(110,119),(131,119),(131,132),(110,132)}
# Reference bare-PCB front is 26.670 mm above the U2 pad-row origin.
u2_front=p.ToMM(footprints['U2'].GetPosition().y)-26.670
assert abs(u2_front-100)<1e-6
assert any(isinstance(g,p.PCB_SHAPE) and g.GetLayer()==p.F_Fab and
g.GetShape()==p.SHAPE_T_SEGMENT and
g.GetStart().y==p.FromMM(100) and g.GetEnd().y==p.FromMM(100)
for g in footprints['U2'].GraphicalItems()), 'U2 bare-PCB front not flush'
assert max(x for x,y in points)-min(x for x,y in points)==82
assert max(y for x,y in points)-min(y for x,y in points)==80
padnets={}
@@ -77,6 +104,13 @@ assert len(parity)==1 and parity[0]["type"]=="footprint_symbol_mismatch",parity
assert len(parity[0]["items"])==1 and parity[0]["items"][0]["uuid"]==footprints["U2"].m_Uuid.AsString()
summary={
"carrier_bounds_mm":[82,80],
"outline_local_mm":expected_outline,
"placements_local_mm":placements,
"rf_reserve_absolute_mm":[110,119,131,132],
"u2_bare_pcb_front_local_y_mm":round(u2_front-100,6),
"nominal_user_confirmed_rs232_metal_mm":{"shell_overhang":3.870,"hex_mouth_overhang":2.870,"flange_front_inset":1.930},
"tracks":sum(not isinstance(t,p.PCB_VIA) for t in tracks),
"vias":sum(isinstance(t,p.PCB_VIA) for t in tracks),
"copper_layers":2,
"draft_thickness_mm":1.6,
"electrical_footprints":len(comps),
@@ -84,9 +118,10 @@ summary={
"physical_DRC_violations":0,
"unconnected_items":0,
"schematic_pad_nets_match":True,
"known_parity_warning":"U2 provisional female-reference footprint assigned on PCB only; schematic footprint intentionally blank",
"known_parity_warning":"U2 reference footprint assigned on PCB only; schematic footprint intentionally blank; nominal male external metal dimensions user-confirmed",
"project_settings_sha256":hashlib.sha256((ROOT/'serial-carrier.kicad_pro').read_bytes()).hexdigest(),
"ignored_DRC_checks":report["ignored_checks"],
"not_validated":["male module pin map/fit", "assembly envelope and heights", "header/socket fit", "RF performance and antenna clearance", "power/return-path integrity", "USB power path", "OLED pull-ups", "fabrication process", "hardware operation"],
"not_validated":["male module pin map/header fit", "ESP32 geometry and complete assembly envelope/heights", "header/socket fit", "RF performance and antenna clearance", "power/return-path integrity", "USB power path", "OLED pull-ups", "fabrication process", "hardware operation"],
}
(OUT/"pcb-validation-summary.json").write_text(json.dumps(summary,indent=2)+"\n")
print("PCB draft verified: 82 x 80 mm; 2 copper layers; 0 physical DRC violations; 0 unrouted.")