component
TPS16850VMAR - 9 V to 80 V, 20 A eFuse
Public Made by Adomby adom
Texas Instruments TPS16850VMAR: 9 V to 80 V, 20 A stackable integrated hot-swap eFuse with a 3.5 mOhm FET, fast current monitor and auto-retry fault response, LQFN-CLIP-23 (VMA, 6.0 x 5.0 mm). KiCad symbol and footprint, Fusion and Altium libraries, datasheet-built 3D model and datasheet.
main
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
#!/usr/bin/env python3
"""Prove the datasheet-built VMA0023A model against TI's PACKAGE OUTLINE drawing (SLVSHA1D p.54, 300 dpi render).
The model STEP is rendered orthographically (ortho.py, face colours kept) in the drawing's projections:
bottom view: TI draws it turned over about the X axis (ortho "bottom": +X right, +Y down);
side view: the drawing under the top view (ortho "front" or "back", whichever fits).
Scale and offset of the bottom view are fitted by maximising the share of the model's terminal and body edge
pixels that land on the drawing's blue strokes (scale searched 100..140 px/mm; it lands on TI's 10X =
118.1 px/mm); the side view uses the same scale with only its offset searched.
Output: <prefix>-bottom.png, <prefix>-side.png (drawing, model at 45 % opacity, model edges in red), <prefix>.json.
usage: overlay_vma.py model.step p54-300dpi.png out_prefix
"""
import sys, json, os
import numpy as np
from PIL import Image, ImageDraw, ImageFont, ImageFilter
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import ortho
step, p54, pre = sys.argv[1:4]
tris = ortho.load_tris(step)
page = Image.open(p54).convert("RGB")
arr = np.asarray(page).astype(int)
blue = (arr[:, :, 2] > 170) & (arr[:, :, 0] < 140) & (arr[:, :, 1] > 60) & (arr[:, :, 1] < 210)
dil = np.array(Image.fromarray(blue).filter(ImageFilter.MaxFilter(3)))
REF = 60.0
def edges(img):
a = np.asarray(img)
alpha = a[:, :, 3] > 128
dark = (a[:, :, :3].sum(2) < 200) & alpha
metal = alpha & ~dark
e = np.zeros_like(alpha)
for m in (alpha, metal):
e |= m & ~np.array(Image.fromarray(m).filter(ImageFilter.MinFilter(3)))
ys, xs = np.nonzero(e)
return np.stack([xs - a.shape[1] / 2, ys - a.shape[0] / 2], 1)
def fit(view, region, scales=None):
P = edges(ortho.render(tris, view, REF, shade=False))
P = P[np.linspace(0, len(P) - 1, min(len(P), 2500)).astype(int)]
x0, y0, x1, y1 = region
best = (-1,)
for s in (scales if scales is not None else np.arange(100, 141, 2.0)):
q0 = P * s / REF
for ox in range(x0, x1, 5):
for oy in range(y0, y1, 5):
q = (q0 + (ox, oy)).astype(int)
ok = (q[:, 0] >= 0) & (q[:, 0] < blue.shape[1]) & (q[:, 1] >= 0) & (q[:, 1] < blue.shape[0])
sc = dil[q[ok, 1], q[ok, 0]].mean() * ok.mean()
if sc > best[0]: best = (sc, s, ox, oy)
sc, s, ox, oy = best
for s2 in ([s] if scales is not None else np.arange(s - 2, s + 2.01, 0.25)):
for ox2 in range(ox - 5, ox + 6):
for oy2 in range(oy - 5, oy + 6):
q = (P * s2 / REF + (ox2, oy2)).astype(int)
v = dil[q[:, 1], q[:, 0]].mean()
if v > sc: sc, s, ox, oy = v, s2, ox2, oy2
return sc, s, ox, oy
def draw(view, sc, s, ox, oy, crop, title, out):
img = ortho.render(tris, view, s)
lay = Image.new("RGBA", page.size, (0, 0, 0, 0))
a = np.asarray(img).copy(); a[:, :, 3] = (a[:, :, 3] * 0.45).astype(np.uint8)
lay.alpha_composite(Image.fromarray(a), (int(ox - img.width / 2), int(oy - img.height / 2)))
P = edges(ortho.render(tris, view, s, shade=False)) + (ox, oy)
d = ImageDraw.Draw(lay)
for x, y in P.astype(int): d.point((x, y), fill=(230, 20, 20, 255))
ov = Image.alpha_composite(page.convert("RGBA"), lay).convert("RGB").crop(crop)
c = Image.new("RGB", (ov.width, ov.height + 80), "white"); c.paste(ov, (0, 0))
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 22)
dd = ImageDraw.Draw(c)
dd.text((16, ov.height + 8), title, fill="black", font=font)
dd.text((16, ov.height + 42), f"fitted scale {s:.1f} px/mm at 300 dpi (TI 10X = 118.1); {sc*100:.1f} % of the model's edge pixels lie on TI's blue strokes", fill="black", font=font)
c.save(out)
res = {}
sc, s, ox, oy = fit("bottom", (1110, 2090, 1280, 2270))
res["bottom"] = dict(score=round(float(sc), 4), scale=round(float(s), 2), origin=[ox, oy], image=os.path.basename(pre) + "-bottom.png")
draw("bottom", sc, s, ox, oy, (int(ox - 3.9 * s), int(oy - 3.2 * s), int(ox + 3.9 * s), int(oy + 3.2 * s)),
"TPS16850VMAR model (bottom, red edges) over TI SLVSHA1D p.54 VMA0023A bottom view", pre + "-bottom.png")
best = None
for view in ("front", "back"):
r = fit(view, (1110, 1380, 1280, 1490), scales=[s])
if best is None or r[0] > best[1][0]: best = (view, r)
view, (sc2, s2, ox2, oy2) = best
res["side"] = dict(view=view, score=round(float(sc2), 4), scale=round(float(s2), 2), origin=[ox2, oy2], image=os.path.basename(pre) + "-side.png",
note="TI draws the side view at A max (1.5 mm); the model is at the nominal 1.4 mm, so its top edge sits 0.1 mm below TI's")
draw(view, sc2, s2, ox2, oy2, (int(ox2 - 4.2 * s2), int(oy2 - 1.6 * s2), int(ox2 + 4.6 * s2), int(oy2 + 1.6 * s2)),
f"TPS16850VMAR model ({view} side) over TI SLVSHA1D p.54 VMA0023A side view", pre + "-side.png")
json.dump(res, open(pre + ".json", "w"), indent=1)
print(json.dumps(res))