component
LW A6SG-V2BA-FK0PN0
Public Unreviewedby adonis
ams OSRAM Standard LEDs - SMD, White SIDELED®, side-emitting. Datasheet-verified pinout, KiCad symbol/footprint/3D.
main
adonis
fix polarity: cathode is the +X pad; adopt the official ams OSRAM geometry STEP for the 3D model and body outline
b657465
1mo ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
#!/usr/bin/env python3
"""
Place the official ams OSRAM geometry STEP into KiCad 3D-model coordinates.
The vendor file LW_A6SG_20091210_geometry.STEP uses its own frame:
X_o lead axis leads centred at +/-1.27 (2.54 spacing), body +/-2.0
Y_o board normal foot underside at -2.575, package top at +1.450
Z_o emission axis lens plane (the r=1.2 circle) at 0, body back at -3.765
KiCad wants z up with the board at z = 0 and the origin on the land pattern. The
lead's flat contact runs Z_o = -3.475 .. -0.975, so its centre is Z_o = -2.225:
x = -X_o
y = Z_o + 2.225 emission -> +y (KiCad mirrors y into footprint space,
z = Y_o + 2.575 so +y here lands on the footprint's -Y, screen up)
det = +1, a proper rotation: no mirroring, every vendor surface normal stays valid.
Rather than rewrite 459 points and 426 directions -- which would also rewrite the
shape representation's own root placement and so apply the transform twice -- the
whole transform is written into that root placement, #1887. Its location, axis
and ref_direction are each referenced exactly once, so every other byte of the
vendor B-rep is passed through untouched.
**Polarity.** The vendor model carries the cathode marking on the -X_o side: a
cluster of top-face features around X_o = -1.25 with no mirror partner at +1.25.
That matches the datasheet front view, which labels the left-hand lead 'Cathode'.
Under this rotation -X_o becomes +x, so on the footprint -- a view looking DOWN
at the board -- the cathode is on the RIGHT when the lens points up the page.
Reading the datasheet's front view as though it were the top view is what flips
this: a bird's-eye view reverses the lead axis.
"""
import os
import re
DY, DZ = 2.225, 2.575
PLACEMENT = 1887 # the shape representation's own datum axis
NUM = r'(-?[\d.]+(?:[Ee][-+]?\d+)?)'
TRIP = r'\(\s*%s,\s*%s,\s*%s\s*\)' % (NUM, NUM, NUM)
def place(src, dst):
txt = open(src).read()
def point(m):
x, y, z = (float(v) for v in m.group(2, 3, 4))
return "%s( %.9f, %.9f, %.9f )" % (m.group(1), -x, z + DY, y + DZ)
def direction(m):
x, y, z = (float(v) for v in m.group(2, 3, 4))
return "%s( %.9f, %.9f, %.9f )" % (m.group(1), -x, z, y)
# Only 3-tuples are model-space geometry; 2-tuples are pcurve parameter
# space and must pass through untouched.
txt, npt = re.subn(r'(CARTESIAN_POINT \([^()]*)' + TRIP, point, txt)
txt, ndir = re.subn(r'(DIRECTION \([^()]*)' + TRIP, direction, txt)
# The pass above also rotated the representation's own datum axis, which
# some readers apply as a transform on top of the geometry and some ignore.
# Pin it to identity so every reader agrees on where the part is. Its three
# entities are referenced only here, so nothing else moves.
m = re.search(r'#%d\s*=\s*AXIS2_PLACEMENT_3D\s*\([^;]*?\)\s*;' % PLACEMENT, txt)
if not m:
raise SystemExit("root placement #%d not found" % PLACEMENT)
# [0] is the entity's own id, so the three operands start at [1].
loc, axis, ref = (int(x) for x in re.findall(r'#(\d+)', m.group(0))[1:4])
for num, kind, vals in ((loc, 'CARTESIAN_POINT', (0.0, 0.0, 0.0)),
(axis, 'DIRECTION', (0.0, 0.0, 1.0)),
(ref, 'DIRECTION', (1.0, 0.0, 0.0))):
uses = len(re.findall(r'#%d\b' % num, txt)) - 1
if uses != 1:
raise SystemExit("#%d is shared (%d uses); resetting it would move "
"geometry as well" % (num, uses))
txt, hit = re.subn(r'#%d\s*=\s*%s\s*\([^;]*?\)\s*;' % (num, kind),
"#%d = %s ( 'NONE', ( %.9f, %.9f, %.9f ) ) ;"
% (num, kind, *vals), txt, count=1)
if hit != 1:
raise SystemExit("#%d is not a %s; the placement operands were "
"misread" % (num, kind))
txt = colourise(txt)
txt = txt.replace("FILE_NAME ('LW_A6SG_20091210_geometry.STEP'",
"FILE_NAME ('lw-a6sg-v2ba-fk0pn0.step'")
open(dst, 'w').write(txt)
return npt, ndir
BODY_COLOUR = (0.930, 0.930, 0.915) # p.1: "white SMT package"
def colourise(txt):
"""Attach one surface style to the solid.
The vendor file carries no presentation data, so a converted GLB comes out
untextured default grey. The part is a single MANIFOLD_SOLID_BREP, so one
colour is all the model can carry; white matches the datasheet's "white SMT
package". Purely additive -- no existing entity is touched.
"""
brep = int(re.search(r'#(\d+)\s*=\s*MANIFOLD_SOLID_BREP', txt).group(1))
ctx = int(re.findall(r'#(\d+)',
re.search(r'ADVANCED_BREP_SHAPE_REPRESENTATION\s*\([^;]*?\)\s*;',
txt).group(0))[-1])
n = max(int(x) for x in re.findall(r'#(\d+)\s*=', txt))
ids = {k: n + i for i, k in enumerate(
('rgb', 'fac', 'fas', 'ssfa', 'sss', 'ssu', 'psa', 'si', 'mdgpr'), 1)}
block = "\n".join([
"#%d = COLOUR_RGB ( 'body', %.6f, %.6f, %.6f ) ;" % (ids['rgb'], *BODY_COLOUR),
"#%d = FILL_AREA_STYLE_COLOUR ( '', #%d ) ;" % (ids['fac'], ids['rgb']),
"#%d = FILL_AREA_STYLE ( '', ( #%d ) ) ;" % (ids['fas'], ids['fac']),
"#%d = SURFACE_STYLE_FILL_AREA ( #%d ) ;" % (ids['ssfa'], ids['fas']),
"#%d = SURFACE_SIDE_STYLE ( '', ( #%d ) ) ;" % (ids['sss'], ids['ssfa']),
"#%d = SURFACE_STYLE_USAGE ( .BOTH., #%d ) ;" % (ids['ssu'], ids['sss']),
"#%d = PRESENTATION_STYLE_ASSIGNMENT ( ( #%d ) ) ;" % (ids['psa'], ids['ssu']),
"#%d = STYLED_ITEM ( 'colour', ( #%d ), #%d ) ;" % (ids['si'], ids['psa'], brep),
"#%d = MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION"
" ( '', ( #%d ), #%d ) ;" % (ids['mdgpr'], ids['si'], ctx),
"",
])
return txt.replace("ENDSEC;\nEND-ISO-10303-21;", block + "ENDSEC;\nEND-ISO-10303-21;")
if __name__ == '__main__':
here = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
npt, ndir = place(os.path.join(here, 'LW_A6SG_20091210_geometry.STEP'),
os.path.join(here, 'lw-a6sg-v2ba-fk0pn0.step'))
print("OK: %d points, %d directions rotated; root placement reset to identity"
% (npt, ndir))