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
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
"""Add nominal STEP-derived metal bounding projections on Dwgs.User only.
Not silkscreen, a courtyard, or a case cutout. See db9-mechanical-notes.md for
source attribution, selected face subsets and the user-confirmed male/female
external-geometry equivalence. No module height or case-wall thickness assumed.
"""
import json
from pathlib import Path
import uuid
import pcbnew as p
ROOT=Path(__file__).resolve().parent
PCB=ROOT/"serial-carrier.kicad_pcb"
NS=uuid.UUID("ba7274a8-c35d-46bc-908d-cd86708fc6c4")
def main():
lock=ROOT/"~serial-carrier.kicad_pcb.lck"
if lock.exists():
raise SystemExit("Close PCB Editor before refreshing the CAD envelopes")
before=PCB.read_bytes()
project=ROOT/"serial-carrier.kicad_pro"
project_before=project.read_bytes()
board=p.LoadBoard(str(PCB))
u2=next(f for f in board.GetFootprints() if f.GetReference()=="U2")
assert u2.GetOrientationDegrees()==0, "Review transform for rotated module"
x0,y0=p.ToMM(u2.GetPosition().x),p.ToMM(u2.GetPosition().y)
data=json.loads((ROOT/"reference/db9-cad/measurements.json").read_text())
owned={str(uuid.uuid5(NS,str(i))) for i in range(64)}
for drawing in list(board.GetDrawings()):
if drawing.m_Uuid.AsString() in owned:
board.Delete(drawing)
count=0
def point(x,y):
# STEP/Eagle axes +Y toward connector; KiCad local Y is downward.
return p.VECTOR2I(p.FromMM(x0+x-1.905),p.FromMM(y0+2.667-y))
def add(item):
nonlocal count
assert count<64
item.SetLayer(p.Dwgs_User)
item.SetUuid(p.KIID(str(uuid.uuid5(NS,str(count)))))
board.Add(item)
count+=1
for feature in ("front_flange_exposed_faces", "shell_outer_including_root_and_lip",
"left_front_hex_post", "right_front_hex_post"):
xmin,ymin,_,xmax,ymax,_=data[feature]["bounds"]
rect=p.PCB_SHAPE(board)
rect.SetShape(p.SHAPE_T_RECT)
rect.SetStart(point(xmin,ymax)); rect.SetEnd(point(xmax,ymin))
rect.SetWidth(p.FromMM(0.1))
add(rect)
for axis in data["front_lock_axes"]:
x=axis["location"][0]
# These mark front-facing Y axes, not holes through the carrier PCB.
line=p.PCB_SHAPE(board)
line.SetShape(p.SHAPE_T_SEGMENT)
line.SetStart(point(x,26.907)); line.SetEnd(point(x,32.707))
line.SetWidth(p.FromMM(0.08))
add(line)
label=p.PCB_TEXT(board)
label.SetText("CAD METAL ENVELOPES - NOT CASE CUTOUT")
label.SetPosition(p.VECTOR2I(p.FromMM(x0+13.97),p.FromMM(y0-17.67)))
label.SetTextSize(p.VECTOR2I(p.FromMM(0.65),p.FromMM(0.65)))
label.SetTextThickness(p.FromMM(0.1))
add(label)
for drawing in board.GetDrawings():
if not isinstance(drawing,p.PCB_TEXT):
continue
value=drawing.GetText()
if value.startswith("DE-9 OUT /"):
drawing.SetPosition(p.VECTOR2I(p.FromMM(x0+13.97),p.FromMM(y0-20.67)))
elif value=="82 x 80 mm / 2 copper layers":
drawing.SetPosition(p.VECTOR2I(p.FromMM(141),p.FromMM(91)))
elif value.startswith("Nominal RS-232 metal verified;"):
drawing.SetPosition(p.VECTOR2I(p.FromMM(141),p.FromMM(93.5)))
temp=ROOT/"serial-carrier-envelope-tmp.kicad_pcb"
try:
assert p.SaveBoard(str(temp),board,True)
if lock.exists() or PCB.read_bytes()!=before:
raise RuntimeError("PCB changed or reopened; refusing overwrite")
assert project.read_bytes()==project_before
temp.replace(PCB)
finally:
temp.unlink(missing_ok=True)
print(f"Added {count} nominal CAD projection graphics on Dwgs.User; no copper/project changes")
if __name__=="__main__":
main()