main
John Lauer Add symbol, footprint, Fusion and Altium libraries, viewers, provenance, README ab389b2 20h ago
#!/usr/bin/env python3
"""Write models.json, lod/manifest.json, provenance/3d-model.json and verification.json for adom/tmcs1123b1aqdvgr
from the files on disk (hashes, sizes, triangle counts). Run from the page root. Adapted from adom/mcp2562fd-e-sn."""
import json, hashlib, os, subprocess
from pygltflib import GLTF2
D = "2026-10-21"; S = "tmcs1123b1aqdvgr"; MPN = "TMCS1123B1AQDVGR"
def sha(p): return hashlib.sha256(open(p, "rb").read()).hexdigest()
def blob(p): return subprocess.check_output(["git", "hash-object", p]).decode().strip()
def glbstats(p):
    g = GLTF2().load(p)
    return dict(triangles=sum(g.accessors[pr.indices].count // 3 for m in g.meshes for pr in m.primitives),
                primitives=sum(len(m.primitives) for m in g.meshes), nodes=[n.name for n in g.nodes if n.mesh is not None])
mark = {"text": "TMCS1123 / B1AQDVGR (two lines)", "surface": "top, along the pin rows, upright with pin 1 lower left",
        "colour": "light grey on black epoxy", "virtual": True,
        "note": "virtual identification mark (the orderable MPN) by adom-chip-laser; TI's real marking is 1123B1A (SBOSA24D p.40)"}
def rec(path, **k):
    r = dict(path=path, format=path.rsplit(".", 1)[1], sha256=sha(path), git_blob=blob(path), bytes=os.path.getsize(path)); r.update(k); return r
models = [
 rec(f"{S}-etched.step", title=f"{MPN}, virtual MPN etch (library model)", status="current", slot="model_3d_step", variant="etched",
     role="datasheet-built", use=["board-cad"], marking=mark, colours=True, derived_from=f"{S}.step",
     unique="The board-CAD model: built from TI's DVG0010A drawing values with the MPN etched on top in the Adom.LaserEtch group; bound by the KiCad footprint, the Fusion .lbr and the Altium PcbLib",
     source="provenance/tools/build_dvg10.py + adom-chip-laser 0.2.0 (service-occt etch, placed by adom-aiflow-etch)",
     thumb=f"docs/models/{S}-etched-iso.png", thumb_bottom=f"docs/models/{S}-etched-bottom.png"),
 rec(f"{S}.step", title=f"{MPN}, unmarked", status="current", slot="model_3d_step_plain", variant="plain", role="datasheet-built",
     use=["board-cad", "archive"], marking=None, colours=True, derived_from="TI SBOSA24D p.45 (DVG0010A, 4226847/C)",
     unique="The same package without any marking", source="provenance/tools/build_dvg10.py (default parameters)", thumb=f"docs/models/{S}-glb-top.png"),
 rec(f"{S}-hero.glb", title=f"{MPN}, annotated hero (page viewer only)", status="current", slot="model_3d", variant="hero",
     role="derived", use=["wiki-hero"], marking=mark, colours=True, derived_from=f"{S}-etched.glb + {S}.kicad_mod",
     unique="The etched model plus adom-hero 1.4 reference artwork (teal dashed pad outlines, silkscreen with pin-1 dot, signal names) and an extra declared layer for the 8.1 mm isolation keepout; not for board use",
     source="adom/component-hero hero_glb.py (adom-hero 1.4) + provenance/tools/hero_post_keepout.py", thumb=f"docs/proof/{S}-hero-preview.png"),
 rec(f"{S}-etched.glb", title=f"{MPN}, virtual MPN etch (GLB, board use)", status="current", slot="model_3d_plain", variant="etched",
     role="derived", use=["render", "board-insertion"], marking=mark, colours=True, derived_from=f"{S}-etched.step",
     unique="Web copy of the etched STEP, clean (no pad outlines, labels or silkscreen), primitives batched per part, matte-tin leads and epoxy body",
     source="step2glb 0.13.2 service --compress none + provenance/tools/glb_batch.py + materials.py", thumb=f"docs/models/{S}-etched-iso.png"),
 rec(f"{S}.glb", title=f"{MPN}, unmarked (GLB)", status="current", variant="plain", role="derived",
     use=["render", "board-insertion"], marking=None, colours=True, derived_from=f"{S}.step",
     unique="Web copy of the unmarked STEP", source="step2glb 0.13.2 service --compress none + glb_batch.py + materials.py", thumb=f"docs/models/{S}-glb-top.png"),
]
lodtxt = {"high": "Identical to the etched GLB", "medium": "Bends as 3 straight segments, etch grafted from the high level",
          "low": "Bends as 3 straight segments, no etch", "super-low": "Undrafted body, one-segment bends, no etch"}
for l in ["high", "medium", "low", "super-low"]:
    p = f"lod/{S}-{l}.glb"
    models.append(rec(p, title=f"{MPN}, {l} detail (board LOD)", status="current", variant="lod", role="derived", use=["board-insertion"],
        marking=mark if l in ("high", "medium") else None, colours=True,
        derived_from=f"{S}-etched.step" if l == "high" else ("provenance/tools/params-low.json" if l in ("medium", "low") else "provenance/tools/params-superlow.json"),
        unique=lodtxt[l], source="build_dvg10.py detail level + step2glb service + glb_batch.py + materials.py", thumb=f"lod/{S}-{l}.png"))
json.dump({"schema": "adom/models@1", "part": MPN, "updated": D, "models": models}, open("models.json", "w"), indent=1)

levels = []
for l in ["high", "medium", "low", "super-low"]:
    p = f"lod/{S}-{l}.glb"; st = glbstats(p)
    levels.append(dict(level=l, path=p, sha256=sha(p), sizeBytes=os.path.getsize(p), triangles=st["triangles"], primitives=st["primitives"],
        nodes=st["nodes"], boundsMm=[-5.15, -5.15, 0.0, 5.15, 5.15, 2.555 if l in ("high", "medium") else 2.55],
        markingIncluded=l in ("high", "medium"), helperNodes=[], extensions=[],
        derivation={"high": "the etched GLB itself: etched STEP, step2glb uncompressed, primitives batched per part (lossless)",
                    "medium": "build_dvg10.py at detail=low (bends as 3 chords, drafted body, pin-1 dimple), Adom.LaserEtch mesh grafted unchanged from high",
                    "low": "build_dvg10.py detail=low, no marking (unreadable at this size)",
                    "super-low": "build_dvg10.py detail=superlow: undrafted body, one-chord bends, pin-1 dimple kept, no marking"}[l],
        review="candidate: inspected as cached images (depth-buffered render); not qualified in a streaming LOD viewer",
        preview=dict(path=f"lod/{S}-{l}.png", sha256=sha(f"lod/{S}-{l}.png"), view="iso (front-left, from above), same camera on every level")))
json.dump({"schema": "adom/component-lod@1", "mpn": MPN, "units": "metres (glTF)",
           "upAxis": "Z (Adom/KiCad frame; seated on z = 0, centred on the footprint origin)", "status": "candidate",
           "source": {"path": f"{S}-etched.step", "sha256": sha(f"{S}-etched.step")},
           "generator": "provenance/tools/build_dvg10.py (detail full/low/superlow) -> step2glb 0.13.2 service convert --compress none -> provenance/tools/glb_batch.py (lossless batching per part; etch graft on medium) -> materials.py; previews by render_glb.py",
           "helpers_removed": "none needed: the LODs are built from the clean etched model, never from the hero",
           "levels": levels}, open("lod/manifest.json", "w"), indent=1)

dm = json.load(open("provenance/dims-model.json")); ds = json.load(open("provenance/dims-model-signal.json"))
ov = json.load(open("provenance/overlay-proof.json"))
dims = [("E overall lead span", "9.97 / - / 10.63 (TYP)", "10.300"),
        ("E1 molded width", "7.4 / - / 7.6", f"{dm['E1_body_width']:.3f}"),
        ("D molded length", "10.1 / - / 10.5", f"{dm['D_body_length']:.3f}"),
        ("A overall height", "max 2.65", f"{dm['A_overall_height']:.3f}"),
        ("A1 standoff", "0.1 / - / 0.3", f"{dm['A1_standoff']:.3f}"),
        ("c lead thickness", "0.23 / - / 0.32 (TYP)", f"{dm['c_lead_thickness']:.3f}"),
        ("b signal lead width (pins 3 to 10)", "0.31 / - / 0.51", f"{ds['b_lead_width'][0]:.3f}"),
        ("e signal pitch", "1.27 (7X)", f"{ds['pitch'][0]:.3f}"),
        ("current lead width (pins 1, 2)", "4.12 / - / 4.32", f"{dm['b_lead_width'][0]:.3f}"),
        ("current lead centre from centre line", "2.44 / - / 2.64", f"{dm['pitch'][0] / 2:.3f}"),
        ("gap between current leads", "(0.86) REF", "0.860"),
        ("L foot length (gauge plane 0.25 mm)", "0.40 / - / 1.27", f"{dm['L_gauge']:.3f}"),
        ("(1.4) lead length, body edge to tip", "(1.4) REF", "1.400"),
        ("lead angle at foot", "0 to 8 deg", "0 (flat foot on the seating plane)")]
prov = {
 "schema": "adom/3d-model-provenance@1", "mpn": MPN, "date": D,
 "classification": "Datasheet-built: a CadQuery model generated from TI's printed DVG0010A package values (SBOSA24D p.45), produced by Claude (AI) for Adom. Not manufacturer CAD and not a TI certification.",
 "method_chosen": "3 (build from the drawing): TI's STEP is behind Ultra Librarian's reCAPTCHA and terms form, and no KiCad library model exists for DVG0010A",
 "manufacturer_step": {"tried": ["https://www.ti.com/product/TMCS1123 (CAD/CAE link)", "https://vendor.ultralibrarian.com/TI/embedded/?gpn=TMCS1123&package=DVG&pin=10"],
   "result": "The Ultra Librarian page lists a STEP export (PartUniqueId 1632f2be-2a1e-11ee-9288-0ae0a3b49db5) but the export form needs a reCAPTCHA and a terms checkbox; a plain HTTP request cannot fetch it (checked 2026-10-21)", "status": "not reachable by plain HTTP"},
 "existing_wiki_model": {"page": "adom/tmcs1123a2aqdvgr (the A2A variant, same DVG package)", "checked": D,
   "verdict": "not reused: its footprint and STEP are KiCad's SOIC-10_3.9x4.9mm_P1mm (narrow 3.9 x 4.9 mm body, 1 mm pitch, five pins per side, pads at x = +-2.66), which is a different package from TI's DVG0010A (7.5 x 10.3 mm body, two 4.22 mm current leads on one side and eight 1.27 mm-pitch leads on the other). The page was not modified."},
 "datasheet": {"doc": "TI SBOSA24D, TMCS1123 (July 2023, revised May 2026)", "file": f"docs/{S}-datasheet.pdf", "url": "https://www.ti.com/lit/ds/symlink/tmcs1123.pdf",
   "sha256": sha(f"docs/{S}-datasheet.pdf"), "drawing_pages": "p.45 package outline DVG0010A (4226847/C 10/2022), p.46 example board layout, p.47 stencil", "marking_page": "p.40"},
 "model": {"file": f"{S}.step", "sha256": sha(f"{S}.step"), "builder": "provenance/tools/build_dvg10.py (CadQuery 2.8, OpenCascade); lead construction from build_soic8.py on adom/mcp2562fd-e-sn",
   "how": "drafted two-part body loft (parting line 1.10 mm), ten swept leads (rectangle b x c along a centreline with two 0.2375 mm centreline-radius bends, 8 deg off vertical): two 4.22 mm current leads at y = +-2.54 on -x, eight 0.41 mm leads at 1.27 mm pitch on +x; pin-1 dimple 0.90 mm diameter, 0.05 mm deep inside the drawing's PIN 1 ID AREA; flat foot 0.50 mm so the gauge-plane L is 0.854 mm",
   "chosen_values": "Where the drawing gives a range, the mid value; A (2.65 MAX, no nominal) modelled as 2.55. Not dimensioned by TI and chosen: parting line height 1.10 mm (read off the side view), draft insets 0.10 / 0.15 mm, inner bend radius 0.10 mm, lead angle 8 deg.",
   "frame": "Z up, seated on z = 0, centred on the KiCad footprint origin, pin 1 at (-x, +y) = footprint pad 1 at (-4.875, -2.54) in KiCad's Y-down footprint frame; binds with zero offset and rotation",
   "parts": {"body": "black epoxy", "pins": "matte tin"},
   "dimensions_mm": [dict(item=a, datasheet_min_typ_max=b, model=c) for a, b, c in dims],
   "measurements": ["provenance/dims-model.json (section at y = +2.54, pin 1)", "provenance/dims-model-signal.json (section at y = +4.445, pin 10; foot section of the +x row)"],
   "overlay_proof": {"image": f"docs/proof/{S}-overlay.png", "report": "provenance/overlay-proof.json", "px_per_mm": ov["px_per_mm"],
     "result": "top, front and end silhouettes blended over the p.45 drawing at the drawing's own scale (calibrated on the printed E and D spans, kx and ky agree within 0.4 %); the leads, the current-lead gap and the body outline coincide with the drawing lines"},
   "pin1_mark": "0.90 mm dimple at (-2.60, +4.00) on the top face, inside TI's hatched PIN 1 ID AREA (p.45); TI does not dimension the feature",
   "not_modelled": "mold flash and interlead flash (notes 3, 4), the exact pin-1 feature shape"},
 "etched": {"file": f"{S}-etched.step", "sha256": sha(f"{S}-etched.step"), "tool": "adom-chip-laser 0.2.0 service (service-occt /laser-etch, flat light mark 5 um above the face, Adom.LaserEtch group), placed by adom-aiflow-etch's planner with the page footprint",
   "lines": ["TMCS1123", "B1AQDVGR"], "capMm": 0.98, "virtual": True, "gate": "adom-aiflow-etch check: PASS",
   "check_image": f"docs/proof/{S}-etched.check.png", "plan": f"docs/proof/{S}-etched.mark-plan.json",
   "real_marking": "TI marks this part 1123B1A (SBOSA24D p.40, Part marking); the etch is the orderable MPN instead, as a virtual identification aid"},
 "glb": {f: sha(f) for f in [f"{S}.glb", f"{S}-etched.glb", f"{S}-hero.glb"]},
 "hero": {"file": f"{S}-hero.glb", "generator": "adom/component-hero skills/component-hero/scripts/hero_glb.py (adom-hero 1.4) then provenance/tools/hero_post_keepout.py",
   "sidecar": f"{S}-hero.overlay.json", "align": "OK, centre offset 0.0 mm", "pin1": "added dot (AI) beside pad 1: the footprint's pin-1 silkscreen bar is a line, which the generator does not count as a mark"},
 "outline": {"file": "artwork/TMCS1123B1AQDVGR-3d-outline-dark-named.svg", "sha256": sha("artwork/TMCS1123B1AQDVGR-3d-outline-dark-named.svg"),
   "tool": "adom-aiflow-etch outline (service body outline + single-stroke name on the planned band)", "gate": "PASS"},
 "tools": {"adom-chip-laser": "0.2.0", "adom-aiflow-etch": "adom-aiflow 0.2.17", "step2glb": "0.13.2 (service)", "adom-symbol": "1.2.6", "adom-footprint": "1.0.26", "adom-lbr": "2.47.6", "cadquery": "2.8.0 (local venv)"},
 "license": "Model, etch and artwork authored by Adom: CC-BY-4.0. The TI datasheet keeps TI's terms."}
json.dump(prov, open("provenance/3d-model.json", "w"), indent=1)

files = {p: sha(p) for p in [f"{S}.kicad_sym", f"{S}.kicad_mod", f"{S}.step", f"{S}-etched.step", f"{S}-etched.glb", f"{S}-hero.glb", f"{S}.lbr",
                             f"altium/{MPN}.SchLib", f"altium/{MPN}.PcbLib", f"docs/{S}-datasheet.pdf"]}
BY = {"name": "Claude (AI), automated checks, not yet reviewed by a person", "handle": "claude"}
def ev(*paths): return [{"label": p.rsplit("/", 1)[-1], "href": p} for p in paths]
def chk(*items): return [{"text": t, "result": r} for t, r in items]
ver = {"schema": "adom/verification@1", "part": MPN,
 "variants": [{"mpn": MPN, "note": "B1A: 25 mV/A, 1.65 V zero-current output; 2000-piece large tape and reel (SBOSA24D p.40)", "stocked": False}],
 "entries": [
  {"date": D, "by": BY, "status": "verified", "title": "Pinout matches the datasheet", "scope": ["symbol"], "variants": ["*"],
   "checks": chk(("Symbol pins match SBOSA24D Table 5-1 (p.4): 1 IN+, 2 IN-, 3 GND, 4 ALERT, 5 VREF, 6 VOUT, 7 OC, 8 VS, 9 VOC, 10 NC", "pass"),
                 ("Groups: Primary side (IN+, IN-) on the left; isolated low-voltage side on the right (Supply, Analog output, Overcurrent, Reserved)", "pass"),
                 ("Electrical types: IN+ and IN- passive (a conductor), VS and GND power, VOUT and VREF output, VOC input, OC and ALERT open collector (open-drain), NC no-connect", "pass"),
                 ("Pin n maps to pad n, read back from the Fusion .lbr and the Altium SchLib/PcbLib; Altium has no no-connect pin type, so NC is Passive there; all Altium pin names are shown", "pass"),
                 ("Every pin sits on the 2.54 mm grid (adom-symbol 1.2.6 put the first pin of each column at y = -2.00; snapped by provenance/tools/snap_symbol.py)", "note")),
   "evidence": ev(f"docs/figures/{S}-ds-p4.png", f"docs/proof/{S}-symbol.png"),
   "files": {k: files[k] for k in [f"{S}.kicad_sym", f"{S}.lbr", f"altium/{MPN}.SchLib"]}},
  {"date": D, "by": BY, "status": "verified", "title": "Footprint equals TI's HV / isolation land pattern", "scope": ["footprint"], "variants": ["*"],
   "checks": chk(("All 10 pads equal the p.46 HV / isolation option: pads 1, 2 are 1.65 x 4.41 mm at (-4.875, -+2.54); pads 3 to 10 are 1.65 x 0.60 mm at x = +4.875, 1.27 mm pitch; R0.05 corners", "pass"),
                 ("Pad-to-pad clearance between the primary and low-voltage rows is 8.1 mm (TI: 8.1 mm clearance/creepage); the part is rated 8 mm (p.6)", "pass"),
                 ("Isolation keepout rule area on all copper layers between the rows (x -4.05 to 4.05, y -5.5 to 5.5); also in the Fusion .lbr as t/b/vRestrict", "pass"),
                 ("TI's IPC-7351 nominal option (7.3 mm) is not used: it is under the part's 8 mm rating", "note"),
                 ("Pads 1 and 2 carry no heatsink vias on purpose: they are the current leads; TI asks for via farms in the copper planes around them (p.36)", "note")),
   "evidence": ev(f"docs/proof/{S}-footprint-check.png", f"docs/figures/{S}-ds-p46.png", "provenance/footprint-check.json"),
   "files": {f"{S}.kicad_mod": files[f"{S}.kicad_mod"], f"{S}.lbr": files[f"{S}.lbr"]}},
  {"date": D, "by": BY, "status": "verified", "title": "Pin 1 orientation agrees across datasheet, footprint, 3D model and symbol", "scope": ["footprint", "3d", "symbol"], "variants": ["*"],
   "checks": chk(("Datasheet top view (p.4 Figure 5-1, p.45): pin 1 IN+ upper left, pin 2 IN- lower left, pins 3 to 10 up the right side with 10 at the top", "pass"),
                 ("Footprint: pad 1 upper left (KiCad Y down), pad 2 lower left, pad 3 lower right, pad 10 upper right; silkscreen pin-1 bar at the upper-left corner; F.Fab chamfer at pin 1", "pass"),
                 ("3D model: pin 1 current lead at (-x, +y) in the Z-up model, which is pad 1 after KiCad's Y flip; pin-1 dimple in the same corner; overlay on p.45 matches", "pass"),
                 ("Hero GLB: adom-hero alignment guard OK (centre offset 0.0 mm); labels IN+ upper left, NC upper right, GND lower right", "pass"),
                 ("Symbol: pin numbers and names as Table 5-1", "pass"),
                 ("The MPN etch reads upright with pin 1 lower left in the planner's standard pose (adom-aiflow-etch gate PASS)", "pass")),
   "evidence": ev(f"docs/proof/{S}-footprint-check.png", f"docs/proof/{S}-overlay.png", f"docs/proof/{S}-hero-preview.png", f"docs/figures/{S}-ds-p45.png"),
   "files": {k: files[k] for k in [f"{S}.kicad_mod", f"{S}-etched.step", f"{S}-hero.glb", f"{S}.kicad_sym"]}},
  {"date": D, "by": BY, "status": "verified", "title": "3D model inside TI's package drawing", "scope": ["3d"], "variants": ["*"],
   "checks": chk(("E 10.30, E1 7.50, D 10.30, A 2.55, A1 0.20, c 0.275, b 0.41, current leads 4.22 at +-2.54: inside DVG0010A (p.45), measured by section", "pass"),
                 ("Foot L 0.854 mm at the 0.25 mm gauge plane: inside 0.40 to 1.27", "pass"),
                 ("Overlay of top, front and end views on the p.45 drawing at its own scale", "pass"),
                 ("TI's STEP (Ultra Librarian) needs a reCAPTCHA; not compared", "note")),
   "evidence": ev(f"docs/proof/{S}-overlay.png", "provenance/3d-model.json", f"docs/figures/{S}-ds-p45.png"),
   "files": {k: files[k] for k in [f"{S}.step", f"{S}-etched.step", f"{S}-etched.glb"]}},
  {"date": D, "by": BY, "status": "open", "title": "Still to do", "scope": ["3d", "library"], "variants": ["*"],
   "checks": chk(("Compare against TI's own STEP once someone downloads it from Ultra Librarian in a browser", "open"),
                 ("Altium PcbLib has no keepout region: adom-lbr 2.47.6 does not carry the KiCad rule area; add a keepout region over x -4.05 to 4.05 mm in Altium, or rely on board rules", "open"),
                 ("Open the KiCad, Fusion and Altium libraries in the native tools; so far they are checked by lint and read-back only", "open"),
                 ("No reel on hand and no distributor stock on 2026-10-21 (Mouser and DigiKey 0, 140-day lead time at Mouser)", "open")),
   "evidence": [], "files": {}}
 ]}
json.dump(ver, open("verification.json", "w"), indent=1)
print("OK")