component
LW A6SG-V2BA-FK0PN0
Public Unreviewedby adonis
ams OSRAM Standard LEDs - SMD, White SIDELED®, side-emitting. Datasheet-verified pinout, KiCad symbol/footprint/3D.
← Commit history
LW A6SG-V2BA-FK0PN0 SIDELED: datasheet-derived symbol, footprint, 3D model, viewers and generators
7 files changed
+1255−22
page.json+116−22@@ -1,8 +1,12 @@ {+ "schema_version": 1,+ "type": "component",+ "kind": "component", "slug": "lw-a6sg-v2ba-fk0pn0",+ "title": "LW A6SG-V2BA-FK0PN0",+ "brief": "ams OSRAM Standard LEDs - SMD, White SIDELED\u00ae, side-emitting. Datasheet-verified pinout, KiCad symbol/footprint/3D.", "version": "0.1.2",- "type": "component",- "description": "ams OSRAM Standard LEDs - SMD, White SIDELED®, side-emitting, 900-2240 mcd at IF = 20 mA. Datasheet-verified pinout, KiCad symbol/footprint/3D.",+ "license": "CC-BY-SA-4.0", "tags": [ "led", "sideled",@@ -11,6 +15,113 @@ "automotive", "lw-a6sg-v2ba-fk0pn0" ],+ "component": {+ "mpn": "LW A6SG-V2BA-FK0PN0",+ "manufacturer": "ams OSRAM",+ "package": "SIDELED\u00ae",+ "pin_count": 2,+ "category": "LED",+ "subcategory": "Standard LEDs - SMD, White SIDELED\u00ae",+ "datasheet": "docs/lw-a6sg-v2ba-fk0pn0-datasheet.pdf",+ "parts": {+ "symbol": "lw-a6sg-v2ba-fk0pn0.kicad_sym",+ "footprint": "lw-a6sg-v2ba-fk0pn0.kicad_mod",+ "model_3d": "lw-a6sg-v2ba-fk0pn0.glb",+ "model_3d_step": "lw-a6sg-v2ba-fk0pn0.step",+ "lbr": "lw-a6sg-v2ba-fk0pn0.lbr"+ },+ "distributor_links": {+ "mouser": "https://www.mouser.com/en/ProductDetail/ams-OSRAM/LW-A6SG-V2BA-FK0PN0?qs=vLWxofP3U2xiZuGh4CU2Qw%3D%3D",+ "digikey": "https://www.digikey.com/en/products/detail/ams-osram-ag/LW-A6SG-V2BA-FK0PN0-0-20-R33/13624925"+ },+ "pins": [+ {+ "number": "1",+ "name": "K",+ "type": "passive",+ "description": "Cathode. The datasheet's dimensional drawing (p.14) labels this lead 'Cathode' and puts the cathode marking on this side of the package; the chamfered body corner in the 3D model and the silkscreen triangle both mark it. Return the LED current here."+ },+ {+ "number": "2",+ "name": "A",+ "type": "passive",+ "description": "Anode. Drive at IF = 20 mA typical, 5 mA min to 50 mA max (p.3). VF is 2.90 V min / 3.00 V typ / 3.50 V max at 20 mA (p.4), so size the series resistor from VF max, not typ. Never connect to a fixed supply without current limiting."+ }+ ],+ "provenance": {+ "extractor": "adom component pipeline (Claude)",+ "datasheet": {+ "url": "docs/lw-a6sg-v2ba-fk0pn0-datasheet.pdf",+ "title": "LW A6SG binning FK0PN0",+ "version": "1.1",+ "dated": "2020-06-01",+ "pages": 25,+ "sha256": "93722427bd3f7bbb",+ "source_tier": "manufacturer",+ "retrieved": "2026-08-27"+ },+ "cad": "Generated from the manufacturer datasheet (land pattern p.15, package outline p.14)",+ "pins": {+ "total": 2,+ "described": 2,+ "cites_pages": true,+ "refuses_to_fabricate": true+ }+ },+ "viewers": {+ "symbol": "viewers/symbol.html",+ "footprint": "viewers/footprint.html"+ },+ "pin1": {+ "pad": "1",+ "at": [+ -1.25,+ 0.0+ ],+ "corner": "left",+ "source": "datasheet-cathode-marking",+ "derived_from": "[email protected]",+ "mark": "drawn"+ }+ },+ "metadata": {+ "specs": {+ "Manufacturer": "ams OSRAM",+ "Package": "SIDELED\u00ae, 2-lead gull-wing, side-emitting",+ "Category": "LED",+ "Pins": "2",+ "Colour": "White, Cx = 0.33 / Cy = 0.33 (CIE 1931)",+ "Luminous intensity": "900 \u2026 2240 mcd at IF = 20 mA",+ "Forward voltage": "3.00 V typ, 3.50 V max at IF = 20 mA",+ "Forward current": "20 mA typ, 50 mA max (5 mA min)",+ "Viewing angle": "120\u00b0 (2\u03c6 at 50 % IV)",+ "Body size": "4.00 \u00d7 3.63 \u00d7 3.97 mm (0.80 mm standoff)"+ }+ },+ "sections": [+ "hero",+ "viewers",+ "readme",+ "breadcrumbs",+ "info"+ ],+ "discovery_triggers": [+ "lw-a6sg-v2ba-fk0pn0",+ "lw a6sg",+ "a6sg",+ "sideled",+ "osram sideled",+ "white sideled",+ "side emitting led",+ "side firing led",+ "q65113a1198",+ "light pipe led"+ ],+ "discovery_pitch": "LW A6SG-V2BA-FK0PN0 (ams OSRAM) white SIDELED - side-emitting LED with datasheet-verified pinout, land pattern, KiCad symbol/footprint and 3D model.",+ "author": {+ "name": "adonis",+ "email": "[email protected]"+ }, "dependencies": {}, "hero": { "type": "image",@@ -24,28 +135,11 @@ "lw-a6sg-v2ba-fk0pn0.glb", "lw-a6sg-v2ba-fk0pn0.step", "lw-a6sg-v2ba-fk0pn0.lbr",+ "docs/hero.png", "docs/lw-a6sg-v2ba-fk0pn0-datasheet.pdf", "viewers/symbol.html", "viewers/footprint.html", "pads.json", "skills/sideled-lw-a6sg/SKILL.md"- ],- "author": {- "name": "adonis",- "email": "[email protected]"- },- "kind": "component",- "discovery_triggers": [- "lw-a6sg-v2ba-fk0pn0",- "lw a6sg",- "a6sg",- "sideled",- "osram sideled",- "white sideled",- "side emitting led",- "side firing led",- "q65113a1198",- "light pipe led"- ],- "discovery_pitch": "LW A6SG-V2BA-FK0PN0 (ams OSRAM) white SIDELED - side-emitting LED with datasheet-verified pinout, land pattern, KiCad symbol/footprint and 3D model."-}+ ]+}
tools/gen_part.pyadded+654@@ -0,0 +1,654 @@+#!/usr/bin/env python3+"""+Emit every non-3D artefact for the LW A6SG-V2BA-FK0PN0 component page from one+source of truth: KiCad symbol + footprint, the EAGLE library, pads.json,+page.json, package.json, README.md and the two interactive viewers.++Datasheet: "LW A6SG binning FK0PN0", ams OSRAM, version 1.1, 2020-06-01.+Page numbers in comments refer to that document.+"""+import json, os, re, textwrap++HERE = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))+SLUG = "lw-a6sg-v2ba-fk0pn0"+MPN = "LW A6SG-V2BA-FK0PN0"+SYM = "LW_A6SG-V2BA-FK0PN0"+FP = "LED_SIDELED_LW-A6SG"+MFR = "ams OSRAM"+PKG = "SIDELED®"+OWNER = "adonis"++# --------------------------------------------------------------- land pattern+# p.15 "Recommended Solder Pad": two 1.2 x 3.0 pads, 3.7 outer-to-outer.+PAD_W, PAD_H, PAD_SPAN = 1.20, 3.00, 3.70+PAD_X = (PAD_SPAN - PAD_W) / 2.0 # 1.25 -> pitch 2.5+BODY_W, BODY_D = 4.00, 3.63 # p.14 top view+LENS_DIA = 2.40 # p.14 front view+CHAMFER = 0.50+SILK_X, SILK_Y = BODY_W / 2 + 0.10, BODY_D / 2 + 0.10+CRTY_X, CRTY_Y = BODY_W / 2 + 0.25, BODY_D / 2 + 0.25++PINS = [+ dict(number="1", name="K", type="passive", pad_x=-PAD_X,+ description=(+ "Cathode. The datasheet's dimensional drawing (p.14) labels this "+ "lead 'Cathode' and puts the cathode marking on this side of the "+ "package; the chamfered body corner in the 3D model and the "+ "silkscreen triangle both mark it. Return the LED current here.")),+ dict(number="2", name="A", type="passive", pad_x=+PAD_X,+ description=(+ "Anode. Drive at IF = 20 mA typical, 5 mA min to 50 mA max (p.3). "+ "VF is 2.90 V min / 3.00 V typ / 3.50 V max at 20 mA (p.4), so "+ "size the series resistor from VF max, not typ. Never connect to "+ "a fixed supply without current limiting.")),+]++SPECS = [+ ("Manufacturer", MFR),+ ("Package", "SIDELED®, 2-lead gull-wing, side-emitting"),+ ("Category", "LED"),+ ("Pins", "2"),+ ("Colour", "White, Cx = 0.33 / Cy = 0.33 (CIE 1931)"),+ ("Luminous intensity", "900 … 2240 mcd at IF = 20 mA"),+ ("Forward voltage", "3.00 V typ, 3.50 V max at IF = 20 mA"),+ ("Forward current", "20 mA typ, 50 mA max (5 mA min)"),+ ("Viewing angle", "120° (2φ at 50 % IV)"),+ ("Body size", "4.00 × 3.63 × 3.97 mm (0.80 mm standoff)"),+]++ELECTRICAL = [+ ("Forward voltage", "VF", "2.90", "3.00", "3.50", "V", "IF = 20 mA (p.4)"),+ ("Forward current", "IF", "5", "20", "50", "mA", "TS = 25 °C (p.3)"),+ ("Surge current", "IFS", "—", "—", "300", "mA",+ "t ≤ 10 µs, D = 0.005 (p.3)"),+ ("Reverse voltage", "VR", "—", "—", "5", "V", "TS = 25 °C (p.3)"),+ ("Reverse current", "IR", "—", "0.01", "10", "µA", "VR = 5 V (p.4)"),+ ("Luminous intensity", "IV", "900", "—", "2240", "mcd",+ "IF = 20 mA, bins V2…BA (p.2, p.5)"),+ ("Chromaticity", "Cx / Cy", "—", "0.33 / 0.33", "—", "—",+ "CIE 1931, bin group FK0PN0 (p.4)"),+ ("Thermal resistance", "RthJS", "—", "—", "220", "K/W",+ "junction to solder point, real (p.4)"),+ ("Thermal resistance", "RthJA", "—", "—", "380", "K/W",+ "junction to ambient, real (p.4)"),+ ("Junction temperature", "Tj", "—", "—", "125", "°C", "p.3"),+ ("Operating temperature", "Top", "-40", "—", "110", "°C", "p.3"),+]+++def w(rel, text):+ path = os.path.join(HERE, rel)+ os.makedirs(os.path.dirname(path), exist_ok=True)+ with open(path, "w") as fh:+ fh.write(text)+ print(" wrote %-42s %6d B" % (rel, len(text.encode())))+++# ------------------------------------------------------------------- symbol+def kicad_sym():+ def pin(p, x, ang, name):+ return (+ ' (pin passive line (at %.2f 0.00 %d) (length 2.54)\n'+ ' (name "%s" (effects (font (size 1.27 1.27))))\n'+ ' (number "%s" (effects (font (size 1.27 1.27))))\n'+ ' )' % (x, ang, name, p))++ stroke = '(stroke (width 0.254) (type default)) (fill (type %s))'+ body = [+ # cathode bar, then the anode triangle pointing into it+ ' (polyline (pts (xy -1.27 -1.27) (xy -1.27 1.27))\n %s)'+ % (stroke % 'none'),+ ' (polyline (pts (xy 1.27 1.27) (xy 1.27 -1.27) (xy -1.27 0.00)'+ ' (xy 1.27 1.27))\n %s)' % (stroke % 'none'),+ ]+ # two emission arrows, the LED convention+ for dx in (-0.55, 0.75):+ body.append(+ ' (polyline (pts (xy %.2f 1.60) (xy %.2f 2.70))\n %s)'+ % (dx, dx + 1.10, stroke % 'none'))+ body.append(+ ' (polyline (pts (xy %.2f 2.70) (xy %.2f 2.24) (xy %.2f 2.34)'+ ' (xy %.2f 2.70))\n %s)'+ % (dx + 1.10, dx + 1.02, dx + 0.64, dx + 1.10, stroke % 'outline'))+ return (+ '(kicad_symbol_lib (version 20211014) (generator adom_sideled)\n'+ ' (symbol "%s" (pin_names (offset 0.762) hide) (in_bom yes) (on_board yes)\n'+ ' (property "Reference" "D" (at 0 5.08 0) (effects (font (size 1.27 1.27))))\n'+ ' (property "Value" "%s" (at 0 -3.81 0) (effects (font (size 1.27 1.27))))\n'+ ' (property "Footprint" "%s:%s" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))\n'+ ' (property "Datasheet" "docs/%s-datasheet.pdf" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))\n'+ ' (property "Manufacturer" "%s" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))\n'+ ' (property "Description" "White SIDELED, side-emitting, 900-2240 mcd at 20 mA" (at 0 0 0) (effects (font (size 1.27 1.27)) hide))\n'+ ' (symbol "%s_0_1"\n%s\n )\n'+ ' (symbol "%s_1_1"\n%s\n )\n'+ ' )\n)\n'+ % (SYM, MPN, SLUG, FP, SLUG, MFR, SYM, "\n".join(body), SYM,+ "\n".join([pin("1", -3.81, 0, "K"), pin("2", 3.81, 180, "A")])))+++# ---------------------------------------------------------------- footprint+def kicad_mod():+ def pad(n, x, name):+ return (+ '\t(pad "%s" smd roundrect\n'+ '\t\t(at %.3f 0)\n\t\t(size %.2f %.2f)\n'+ '\t\t(layers "F.Cu" "F.Paste" "F.Mask")\n'+ '\t\t(roundrect_rratio 0.25)\n'+ '\t\t(uuid "0000pad-%s")\n\t)' % (n, x, PAD_W, PAD_H, n))++ def line(layer, x1, y1, x2, y2, width):+ return ('\t(fp_line\n\t\t(start %.3f %.3f)\n\t\t(end %.3f %.3f)\n'+ '\t\t(stroke\n\t\t\t(width %.3f)\n\t\t\t(type solid)\n\t\t)\n'+ '\t\t(layer "%s")\n\t)' % (x1, y1, x2, y2, width, layer))++ def rect(layer, hx, hy, width):+ return "\n".join([+ line(layer, -hx, -hy, hx, -hy, width),+ line(layer, hx, -hy, hx, hy, width),+ line(layer, hx, hy, -hx, hy, width),+ line(layer, -hx, hy, -hx, -hy, width)])++ hx, hy = BODY_W / 2, BODY_D / 2+ fab = "\n".join([+ line("F.Fab", -hx, -hy + CHAMFER, -hx + CHAMFER, -hy, 0.10),+ line("F.Fab", -hx + CHAMFER, -hy, hx, -hy, 0.10),+ line("F.Fab", hx, -hy, hx, hy, 0.10),+ line("F.Fab", hx, hy, -hx, hy, 0.10),+ line("F.Fab", -hx, hy, -hx, -hy + CHAMFER, 0.10),+ # lens window on the emitting (-Y) face, and the emission arrow+ '\t(fp_circle\n\t\t(center 0 %.3f)\n\t\t(end %.3f %.3f)\n'+ '\t\t(stroke\n\t\t\t(width 0.100)\n\t\t\t(type solid)\n\t\t)\n'+ '\t\t(fill none)\n\t\t(layer "F.Fab")\n\t)' % (-hy, LENS_DIA / 2, -hy),+ line("F.Fab", 0, -hy - 0.35, 0, -hy - 1.15, 0.10),+ line("F.Fab", 0, -hy - 1.15, -0.30, -hy - 0.85, 0.10),+ line("F.Fab", 0, -hy - 1.15, 0.30, -hy - 0.85, 0.10),+ ])+ silk = rect("F.SilkS", SILK_X, SILK_Y, 0.12) + "\n" + (+ # cathode / pin-1 triangle, outside the body on the -X side+ '\t(fp_poly\n\t\t(pts\n\t\t\t(xy %.3f %.3f) (xy %.3f %.3f) (xy %.3f %.3f)\n\t\t)\n'+ '\t\t(stroke\n\t\t\t(width 0.050)\n\t\t\t(type solid)\n\t\t)\n'+ '\t\t(fill solid)\n\t\t(layer "F.SilkS")\n\t)'+ % (-PAD_X, -SILK_Y - 0.10, -PAD_X - 0.24, -SILK_Y - 0.53,+ -PAD_X + 0.24, -SILK_Y - 0.53))+ crtyd = rect("F.CrtYd", CRTY_X, CRTY_Y, 0.05)++ def prop(name, value, layer, y, hide=""):+ return ('\t(property "%s" "%s"\n\t\t(at 0 %.3f 0)\n\t\t(layer "%s")\n%s'+ '\t\t(uuid "0000prop-%s")\n\t\t(effects\n\t\t\t(font\n'+ '\t\t\t\t(size 1 1)\n\t\t\t\t(thickness 0.15)\n\t\t\t)\n\t\t)\n\t)'+ % (name, value, y, layer, hide, name.lower()))++ return (+ '(footprint "%s"\n'+ '\t(version 20240108)\n'+ '\t(generator "adom_sideled")\n'+ '\t(generator_version "9.0")\n'+ '\t(layer "F.Cu")\n'+ '\t(descr "ams OSRAM SIDELED, 2-lead side-emitting LED, 4.0 x 3.63 x 3.97 mm. '+ 'Land pattern per the Recommended Solder Pad drawing, LW A6SG binning FK0PN0 v1.1 p.15: '+ '2 x (1.2 x 3.0) mm pads, 3.7 mm outer span. Emission is toward -Y.")\n'+ '\t(tags "LED SIDELED OSRAM side-emitting white")\n'+ '\t(attr smd)\n'+ '%s\n%s\n%s\n%s\n%s\n%s\n%s\n'+ '\t(embedded_fonts no)\n'+ '\t(model "${KIPRJMOD}/%s.step"\n'+ '\t\t(offset\n\t\t\t(xyz 0 0 0)\n\t\t)\n'+ '\t\t(scale\n\t\t\t(xyz 1 1 1)\n\t\t)\n'+ '\t\t(rotate\n\t\t\t(xyz 0 0 0)\n\t\t)\n\t)\n)\n'+ % (FP,+ prop("Reference", "D**", "F.SilkS", -3.10),+ prop("Value", MPN, "F.Fab", 3.10),+ prop("Datasheet", "docs/%s-datasheet.pdf" % SLUG, "F.Fab", 0,+ "\t\t(hide yes)\n"),+ silk, fab, crtyd,+ "\n".join([pad(p["number"], p["pad_x"], p["name"]) for p in PINS]),+ SLUG))+++# ------------------------------------------------------------------ EAGLE+def lbr():+ layers = "".join(+ '<layer number="%d" name="%s" color="%d" fill="%d" visible="%s" active="yes"/>\n'+ % t for t in [+ (1, "Top", 4, 1, "yes"), (16, "Bottom", 1, 1, "yes"),+ (17, "Pads", 2, 1, "yes"), (18, "Vias", 2, 1, "yes"),+ (20, "Dimension", 15, 1, "yes"), (21, "tPlace", 7, 1, "yes"),+ (22, "bPlace", 7, 1, "yes"), (23, "tOrigins", 15, 1, "yes"),+ (25, "tNames", 7, 1, "yes"), (27, "tValues", 7, 1, "yes"),+ (29, "tStop", 7, 3, "no"), (31, "tCream", 7, 4, "no"),+ (39, "tKeepout", 4, 11, "no"), (51, "tDocu", 7, 1, "yes"),+ (94, "Symbols", 4, 1, "yes"), (95, "Names", 7, 1, "yes"),+ (96, "Values", 7, 1, "yes"), (97, "Info", 7, 1, "yes")])+ # EAGLE's Y axis points the opposite way to KiCad's, hence the sign flips.+ smds = "".join(+ ' <smd name="%s" x="%.4f" y="0.0000" dx="%.4f" dy="%.4f" layer="1" roundness="25"/>\n'+ % (p["number"], p["pad_x"], PAD_W, PAD_H) for p in PINS)+ hx, hy = BODY_W / 2, BODY_D / 2+ wires = "".join(+ ' <wire x1="%.4f" y1="%.4f" x2="%.4f" y2="%.4f" width="%.4f" layer="%d"/>\n' % t+ for t in [+ (-SILK_X, -SILK_Y, SILK_X, -SILK_Y, 0.12, 21),+ (SILK_X, -SILK_Y, SILK_X, SILK_Y, 0.12, 21),+ (SILK_X, SILK_Y, -SILK_X, SILK_Y, 0.12, 21),+ (-SILK_X, SILK_Y, -SILK_X, -SILK_Y, 0.12, 21),+ (-hx, hy - CHAMFER, -hx + CHAMFER, hy, 0.10, 51),+ (-hx + CHAMFER, hy, hx, hy, 0.10, 51),+ (hx, hy, hx, -hy, 0.10, 51),+ (hx, -hy, -hx, -hy, 0.10, 51),+ (-hx, -hy, -hx, hy - CHAMFER, 0.10, 51)])+ return (+ '<?xml version="1.0" encoding="utf-8"?>\n'+ '<!DOCTYPE eagle SYSTEM "eagle.dtd">\n<eagle version="9.6.2">\n<drawing>\n'+ '<settings><setting alwaysvectorfont="no"/></settings>\n'+ '<grid distance="0.1" unitdist="inch" unit="inch" style="lines" multiple="1"'+ ' display="no" altdistance="0.01" altunitdist="inch" altunit="inch"/>\n'+ '<layers>\n%s</layers>\n<library>\n'+ '<description>%s — %s %s</description>\n'+ '<packages>\n<package name="%s">\n'+ ' <description>%s SIDELED, 2-lead side-emitting LED</description>\n'+ '%s%s'+ ' <circle x="0" y="%.4f" radius="%.4f" width="0.1" layer="51"/>\n'+ ' <text x="0" y="%.3f" size="1.27" layer="25" align="bottom-center">>NAME</text>\n'+ ' <text x="0" y="%.3f" size="1.27" layer="27" align="top-center">>VALUE</text>\n'+ '<!--adom-pin1--><polygon width="0.0500" layer="21">'+ '<vertex x="%.4f" y="%.4f"/><vertex x="%.4f" y="%.4f"/>'+ '<vertex x="%.4f" y="%.4f"/></polygon>\n'+ '</package>\n</packages>\n'+ '<symbols>\n<symbol name="%s">\n'+ ' <wire x1="-1.27" y1="-1.27" x2="-1.27" y2="1.27" width="0.254" layer="94"/>\n'+ ' <wire x1="1.27" y1="1.27" x2="1.27" y2="-1.27" width="0.254" layer="94"/>\n'+ ' <wire x1="1.27" y1="-1.27" x2="-1.27" y2="0" width="0.254" layer="94"/>\n'+ ' <wire x1="-1.27" y1="0" x2="1.27" y2="1.27" width="0.254" layer="94"/>\n'+ ' <wire x1="-0.55" y1="1.6" x2="0.55" y2="2.7" width="0.2" layer="94"/>\n'+ ' <wire x1="0.75" y1="1.6" x2="1.85" y2="2.7" width="0.2" layer="94"/>\n'+ ' <text x="0" y="5.08" size="1.27" layer="95" align="bottom-center">>NAME</text>\n'+ ' <text x="0" y="-3.81" size="1.27" layer="96" align="top-center">>VALUE</text>\n'+ ' <pin name="K" x="-3.81" y="0" length="short" direction="pas"/>\n'+ ' <pin name="A" x="3.81" y="0" length="short" direction="pas" rot="R180"/>\n'+ '</symbol>\n</symbols>\n'+ '<devicesets>\n<deviceset name="%s" prefix="D">\n'+ ' <description>%s white SIDELED, 900-2240 mcd at IF = 20 mA</description>\n'+ ' <gates><gate name="G$1" symbol="%s" x="0" y="0"/></gates>\n'+ ' <devices>\n <device name="" package="%s">\n <connects>\n'+ ' <connect gate="G$1" pin="K" pad="1"/>\n'+ ' <connect gate="G$1" pin="A" pad="2"/>\n'+ ' </connects>\n'+ ' <technologies><technology name=""/></technologies>\n'+ ' </device>\n </devices>\n</deviceset>\n</devicesets>\n'+ '</library>\n</drawing>\n</eagle>\n'+ % (layers, MPN, MFR, PKG, FP, MFR, smds, wires,+ hy, LENS_DIA / 2, SILK_Y + 0.7, -SILK_Y - 0.7,+ -PAD_X, SILK_Y + 0.10, -PAD_X - 0.24, SILK_Y + 0.53,+ -PAD_X + 0.24, SILK_Y + 0.53,+ SYM, SYM, MFR, SYM, FP))+++# ------------------------------------------------------------------- viewers+TPL = open(os.path.join(HERE, "tools", "viewer.tpl.html")).read()+++def viewer(kind, bar, svg, title):+ pins = [dict(number=p["number"], name=p["name"], type=p["type"],+ desc=p["description"]) for p in PINS]+ return (TPL+ .replace("@@TITLE@@", title)+ .replace("@@BAR@@", bar)+ .replace("@@SVG@@", svg)+ .replace("@@KIND@@", kind)+ .replace("@@MPN@@", "%s/%s" % (OWNER.upper(), SYM))+ .replace("@@PINS@@", json.dumps(pins, sort_keys=True)))+++def symbol_svg():+ S = 100.0 # px per mm+ def X(v): return v * S+ def Y(v): return -v * S # SVG y grows downward, KiCad symbol y grows up+ out = []+ out.append("<polyline points='%.1f,%.1f %.1f,%.1f' fill='none' stroke='#e35d5d' stroke-width='4'/>"+ % (X(-1.27), Y(-1.27), X(-1.27), Y(1.27)))+ out.append("<polygon points='%.1f,%.1f %.1f,%.1f %.1f,%.1f' fill='#e35d5d22' stroke='#e35d5d' stroke-width='4'/>"+ % (X(1.27), Y(1.27), X(1.27), Y(-1.27), X(-1.27), Y(0)))+ for dx in (-0.55, 0.75):+ out.append("<polyline points='%.1f,%.1f %.1f,%.1f' fill='none' stroke='#dfe4ec' stroke-width='4'/>"+ % (X(dx), Y(1.6), X(dx + 1.1), Y(2.7)))+ out.append("<polygon points='%.1f,%.1f %.1f,%.1f %.1f,%.1f' fill='#dfe4ec'/>"+ % (X(dx + 1.10), Y(2.70), X(dx + 1.02), Y(2.24), X(dx + 0.64), Y(2.34)))+ for p, x0, x1 in ((PINS[0], -3.81, -1.27), (PINS[1], 3.81, 1.27)):+ out.append("<g class='pingroup' data-num='%s' data-name='%s'>"+ "<line class='pinvis' x1='%.1f' y1='%.1f' x2='%.1f' y2='%.1f' stroke='#dfe4ec' stroke-width='4'/>"+ "<line x1='%.1f' y1='%.1f' x2='%.1f' y2='%.1f' stroke='transparent' stroke-width='26' pointer-events='stroke'/></g>"+ % (p["number"], p["name"], X(x0), Y(0), X(x1), Y(0),+ X(x0), Y(0), X(x1), Y(0)))+ out.append("<text x='%.1f' y='%.1f' font-size='60' fill='#9fb0c8' text-anchor='middle' dominant-baseline='central'>%s</text>"+ % (X((x0 + x1) / 2), Y(0.45), p["number"]))+ out.append("<text x='%.1f' y='%.1f' font-size='60' fill='#e6e9ef' text-anchor='middle' dominant-baseline='central'>%s</text>"+ % (X((x0 + x1) / 2), Y(-0.55), p["name"]))+ return ("<svg xmlns='http://www.w3.org/2000/svg' viewBox='-560 -340 1120 640'"+ " style='width:100%%;height:100%%'>%s</svg>" % "".join(out))+++def footprint_svg():+ out = []+ def line(cls, colour, x1, y1, x2, y2, wid):+ out.append("<line class='%s' x1='%.3f' y1='%.3f' x2='%.3f' y2='%.3f'"+ " stroke='%s' stroke-width='%.3f'/>"+ % (cls, x1, y1, x2, y2, colour, wid))+ hx, hy = BODY_W / 2, BODY_D / 2+ for a, b, c, d in ((-SILK_X, -SILK_Y, SILK_X, -SILK_Y),+ (SILK_X, -SILK_Y, SILK_X, SILK_Y),+ (SILK_X, SILK_Y, -SILK_X, SILK_Y),+ (-SILK_X, SILK_Y, -SILK_X, -SILK_Y)):+ line("ly-silk", "#d8b4fe", a, b, c, d, 0.12)+ for a, b, c, d in ((-CRTY_X, -CRTY_Y, CRTY_X, -CRTY_Y),+ (CRTY_X, -CRTY_Y, CRTY_X, CRTY_Y),+ (CRTY_X, CRTY_Y, -CRTY_X, CRTY_Y),+ (-CRTY_X, CRTY_Y, -CRTY_X, -CRTY_Y)):+ line("ly-courtyard", "#4ade80", a, b, c, d, 0.05)+ for a, b, c, d in ((-hx, -hy + CHAMFER, -hx + CHAMFER, -hy),+ (-hx + CHAMFER, -hy, hx, -hy),+ (hx, -hy, hx, hy), (hx, hy, -hx, hy),+ (-hx, hy, -hx, -hy + CHAMFER)):+ line("ly-fab", "#f472b6", a, b, c, d, 0.10)+ out.append("<circle class='ly-fab' cx='0' cy='%.3f' r='%.3f' fill='none'"+ " stroke='#f472b6' stroke-width='0.10'/>" % (-hy, LENS_DIA / 2))+ for a, b, c, d in ((0, -hy - 0.35, 0, -hy - 1.15),+ (0, -hy - 1.15, -0.30, -hy - 0.85),+ (0, -hy - 1.15, 0.30, -hy - 0.85)):+ line("ly-fab", "#f472b6", a, b, c, d, 0.10)+ for p in PINS:+ out.append("<rect class='ly-copper pad' data-num='%s' data-name='%s'"+ " x='%.3f' y='%.3f' width='%.3f' height='%.3f' rx='%.3f'"+ " fill='#f59e0b' stroke='#00000033' stroke-width='0.03' opacity='0.94'/>"+ % (p["number"], p["name"], p["pad_x"] - PAD_W / 2, -PAD_H / 2,+ PAD_W, PAD_H, PAD_W * 0.25))+ out.append("<text class='padnum' x='%.3f' y='0' font-size='0.540' fill='#1c1206'"+ " text-anchor='middle' dominant-baseline='central' pointer-events='none'"+ " font-family='ui-monospace,monospace' font-weight='700'>%s</text>"+ % (p["pad_x"], p["number"]))+ out.append("<polygon class='ly-silk adom-pin1' points='%.3f,%.3f %.3f,%.3f %.3f,%.3f'"+ " fill='#d8b4fe' stroke='none'><title>pin 1 (cathode)</title></polygon>"+ % (-PAD_X, -SILK_Y - 0.10, -PAD_X - 0.24, -SILK_Y - 0.53,+ -PAD_X + 0.24, -SILK_Y - 0.53))+ pad = 0.45+ vw, vh = 2 * CRTY_X + 2 * pad, 2 * CRTY_Y + 2 * pad + 0.6+ return ("<svg xmlns='http://www.w3.org/2000/svg' viewBox='%.3f %.3f %.3f %.3f'"+ " style='width:100%%;height:100%%'>%s</svg>"+ % (-CRTY_X - pad, -CRTY_Y - pad - 0.6, vw, vh, "".join(out)))+++# --------------------------------------------------------------------- JSON+def pads_json():+ return json.dumps({+ "schema": "adom.pads/1",+ "slug": SLUG,+ "footprint": FP,+ "generated_from": {+ "footprint": "datasheet:[email protected]",+ "pins": "page.json:component.pins",+ "overrides": None,+ },+ "pin1": {"pad": "1", "at": [-PAD_X, 0.0], "corner": "left",+ "source": "datasheet-cathode-marking",+ "derived_from": "[email protected]"},+ "pad_count": 2,+ "symbol_pins": [p["number"] for p in PINS],+ "paste_apertures": 0,+ "issues": [],+ "pads": [{+ "pad": p["number"], "kind": "smd", "shape": "roundrect",+ "at": [p["pad_x"], 0.0], "rot": 0.0, "size": [PAD_W, PAD_H],+ "layers": ["F.Cu", "F.Mask", "F.Paste"], "thermal": False,+ "pin": p["number"], "name": p["name"], "type": p["type"],+ "description": p["description"], "is_pin1": p["number"] == "1",+ "source": {+ "geometry": "LW A6SG binning FK0PN0 v1.1, p.15 Recommended Solder Pad",+ "meaning": "page.json:component.pins",+ "correspondence": "kicad: symbol pin number == pad name"},+ } for p in PINS],+ }, indent=1) + "\n"+++FILES = ["page.json", "README.md",+ "%s.kicad_sym" % SLUG, "%s.kicad_mod" % SLUG,+ "%s.glb" % SLUG, "%s.step" % SLUG, "%s.lbr" % SLUG,+ "docs/hero.png", "docs/%s-datasheet.pdf" % SLUG,+ "viewers/symbol.html", "viewers/footprint.html", "pads.json",+ "skills/sideled-lw-a6sg/SKILL.md"]+++def page_json(sha):+ return json.dumps({+ "schema_version": 1,+ "type": "component",+ "kind": "component",+ "slug": SLUG,+ "title": MPN,+ "brief": "ams OSRAM Standard LEDs - SMD, White SIDELED®, side-emitting. "+ "Datasheet-verified pinout, KiCad symbol/footprint/3D.",+ "version": "0.1.2",+ "license": "CC-BY-SA-4.0",+ "tags": ["led", "sideled", "side-emitting", "kicad", "automotive", SLUG],+ "component": {+ "mpn": MPN,+ "manufacturer": MFR,+ "package": PKG,+ "pin_count": 2,+ "category": "LED",+ "subcategory": "Standard LEDs - SMD, White SIDELED®",+ "datasheet": "docs/%s-datasheet.pdf" % SLUG,+ "parts": {+ "symbol": "%s.kicad_sym" % SLUG,+ "footprint": "%s.kicad_mod" % SLUG,+ "model_3d": "%s.glb" % SLUG,+ "model_3d_step": "%s.step" % SLUG,+ "lbr": "%s.lbr" % SLUG,+ },+ "distributor_links": {+ "mouser": "https://www.mouser.com/en/ProductDetail/ams-OSRAM/LW-A6SG-V2BA-FK0PN0?qs=vLWxofP3U2xiZuGh4CU2Qw%3D%3D",+ "digikey": "https://www.digikey.com/en/products/detail/ams-osram-ag/LW-A6SG-V2BA-FK0PN0-0-20-R33/13624925",+ },+ "pins": [{"number": p["number"], "name": p["name"],+ "type": p["type"], "description": p["description"]}+ for p in PINS],+ "provenance": {+ "extractor": "adom component pipeline (Claude)",+ "datasheet": {+ "url": "docs/%s-datasheet.pdf" % SLUG,+ "title": "LW A6SG binning FK0PN0",+ "version": "1.1",+ "dated": "2020-06-01",+ "pages": 25,+ "sha256": sha[:16],+ "source_tier": "manufacturer",+ "retrieved": "2026-08-27",+ },+ "cad": "Generated from the manufacturer datasheet "+ "(land pattern p.15, package outline p.14)",+ "pins": {"total": 2, "described": 2, "cites_pages": True,+ "refuses_to_fabricate": True},+ },+ "viewers": {"symbol": "viewers/symbol.html",+ "footprint": "viewers/footprint.html"},+ "pin1": {"pad": "1", "at": [-PAD_X, 0.0], "corner": "left",+ "source": "datasheet-cathode-marking",+ "derived_from": "[email protected]",+ "mark": "drawn"},+ },+ "metadata": {"specs": dict(SPECS)},+ "sections": ["hero", "viewers", "readme", "breadcrumbs", "info"],+ "discovery_triggers": TRIGGERS,+ "discovery_pitch": PITCH,+ "author": {"name": "adonis", "email": "[email protected]"},+ "dependencies": {},+ "hero": {"type": "image", "path": "docs/hero.png"},+ "files": FILES,+ }, indent=2) + "\n"+++TRIGGERS = [SLUG, "lw a6sg", "a6sg", "sideled", "osram sideled",+ "white sideled", "side emitting led", "side firing led",+ "q65113a1198", "light pipe led"]+PITCH = ("LW A6SG-V2BA-FK0PN0 (ams OSRAM) white SIDELED - side-emitting LED "+ "with datasheet-verified pinout, land pattern, KiCad symbol/footprint "+ "and 3D model.")+++def package_json():+ return json.dumps({+ "slug": SLUG,+ "version": "0.1.2",+ "type": "component",+ "description": "ams OSRAM Standard LEDs - SMD, White SIDELED®, "+ "side-emitting, 900-2240 mcd at IF = 20 mA. "+ "Datasheet-verified pinout, KiCad symbol/footprint/3D.",+ "tags": ["led", "sideled", "side-emitting", "kicad", "automotive", SLUG],+ "kind": "component",+ "discovery_triggers": TRIGGERS,+ "discovery_pitch": PITCH,+ "dependencies": {},+ "hero": {"type": "image", "path": "docs/hero.png"},+ # The hero lives in the page repo after the first publish materialised+ # it; keeping it out of the tarball stops every installer re-downloading+ # 1 MB of page art on each update.+ "files": [f for f in FILES if f != "docs/hero.png"],+ }, indent=2) + "\n"+++def readme(sha):+ pin_rows = "\n".join(+ "| %s | %s | %s | %s |" % (p["number"], p["name"], p["type"],+ p["description"]) for p in PINS)+ spec_rows = "\n".join("| %s | %s |" % kv for kv in SPECS)+ el_rows = "\n".join("| %s | %s | %s | %s | %s | %s | %s |" % r+ for r in ELECTRICAL)+ return """# %(mpn)s++**%(mfr)s** · Standard LEDs - SMD, White SIDELED® · %(pkg)s++A white, side-emitting SIDELED® from %(mfr)s: light leaves through the domed+lens on one *vertical* face, parallel to the board, rather than upward. ThinGaN+chip, diffused silicone resin, 120° Lambertian, binned to Cx = Cy = 0.33 and+900–2240 mcd at 20 mA. Ordering code Q65113A1198.++Pinout, land pattern and body dimensions are read off the manufacturer+datasheet; the symbol, footprint and 3D model are generated from those numbers.++## Pinout++| Pin | Name | Type | Function |+|---|---|---|---|+%(pins)s++Pin 1 is the cathode. The package carries a cathode marking on that side+(datasheet p.14); the footprint repeats it as a filled silkscreen triangle and+the 3D model as a chamfered body corner.++## Key specifications++| Parameter | Value |+|---|---|+%(specs)s++## Electrical and optical++| Parameter | Symbol | Min | Typ | Max | Unit | Conditions |+|---|---|---|---|---|---|---|+%(elec)s++Brightness bins for this ordering code: V2 (900–1120 mcd), AA (1120–1400),+AB (1400–1800), BA (1800–2240), all at IF = 20 mA (p.5). Chromaticity bin+group FK0…PN0 (p.6–8). MSL 2 per JEDEC J-STD-020E, reflow peak 245 °C+(p.16). ESD 2 kV HBM Class 2. Corrosion robustness class 1B. Approximate+weight 67.0 mg.++## Land pattern++From the datasheet's *Recommended Solder Pad* drawing (p.15):++| | Value |+|---|---|+| Pads | 2 × SMD roundrect, 1.20 × 3.00 mm |+| Outer span | 3.70 mm (pads centred at x = ±1.25 mm) |+| Copper area | > 16 mm² per pad recommended for heat dissipation |+| Courtyard | 4.50 × 4.13 mm |++Lead spacing on the part is 2.54 mm (p.14) while the recommended land is drawn+on a 2.50 mm pitch — the 0.04 mm difference sits well inside the 1.2 mm pad for+a 1.0 mm lead, and the footprint follows the manufacturer's land drawing.++The datasheet asks for a nitrogen reflow atmosphere for best joint quality, and+the package is **not** suitable for ultrasonic cleaning (p.15).++## CAD++- **Symbol** `%(sym)s` — standard 2-pin LED, pin 1 K / pin 2 A.+- **Footprint** `%(fp)s` — emission direction is **−Y**, marked with an arrow+ and the Ø2.4 mm lens window on F.Fab.+- **3D** `%(slug)s.step` (+ GLB) — 4.00 × 3.63 × 4.77 mm envelope including the+ 0.80 mm lead standoff, origin on the land pattern, part sitting on Z = 0.+- **EAGLE** `%(slug)s.lbr` — same package, symbol and device set.++## Provenance++Everything here is traceable to the manufacturer datasheet rather than a+distributor listing:++- **Datasheet:** [%(mfr)s, *LW A6SG binning FK0PN0*, v1.1, 2020-06-01](docs/%(slug)s-datasheet.pdf)+ (25 pp, sha256 `%(sha)s`, retrieved 2026-08-27)+- **Pins and electricals:** read from pp. 2–6 and 14–16; every row above cites+ its page.+- **Land pattern:** p.15 *Recommended Solder Pad*, used as drawn.+- **Body outline:** p.14 *Dimensional Drawing* — 4 ±0.2 × 3.63 ±0.2 mm plan,+ 3.97 +0.23/−0.17 mm tall, 2.85 mm body depth, 0.80 mm standoff, Ø2.4 mm lens,+ 2.54 mm lead spacing, 1 ±0.1 mm lead width.++Two things the datasheet does not dimension, chosen and recorded here rather+than invented silently:++1. **Body-to-land offset.** The solder-pad drawing gives no body-relative+ origin, so the 3.63 mm body envelope is centred on the land pattern.+2. **Lead-frame thickness (0.20 mm) and foot length**, and the size of the+ cathode chamfer (0.50 mm), are representative — they affect the 3D model's+ appearance only, never the land pattern or the pinout.++## Sourcing++| Distributor | Part number | Note |+|---|---|---|+| Mouser | 720-LWA6SGA1366 | %(mpn)s |+| Mouser | 720-LWA6SGA1198 | LW A6SG-V2BA-FK0PN0-0-20-R33 (taped) |+| DigiKey | 475-LWA6SG-V2BA-FK0PN0-0-20-R33TR-ND | taped variant, in stock |++The bare `%(mpn)s` ordering code is marked **obsolete / not recommended for new+designs**; the `-0-20-R33` tape-and-reel variant is the one still stocked. Check+availability before committing a design.++[Datasheet PDF](docs/%(slug)s-datasheet.pdf)+""" % dict(mpn=MPN, mfr=MFR, pkg=PKG, pins=pin_rows, specs=spec_rows,+ elec=el_rows, sym=SYM, fp=FP, slug=SLUG, sha=sha[:16])+++if __name__ == "__main__":+ import hashlib+ sha = hashlib.sha256(+ open(os.path.join(HERE, "docs", "%s-datasheet.pdf" % SLUG), "rb").read()+ ).hexdigest()+ print("datasheet sha256 %s" % sha)+ w("%s.kicad_sym" % SLUG, kicad_sym())+ w("%s.kicad_mod" % SLUG, kicad_mod())+ w("%s.lbr" % SLUG, lbr())+ w("pads.json", pads_json())+ w("page.json", page_json(sha))+ w("package.json", package_json())+ w("README.md", readme(sha))+ w("viewers/symbol.html",+ viewer("symbol", "<span class=h>Schematic symbol</span><span class=sp></span>",+ symbol_svg(), "%s/%s symbol" % (OWNER.upper(), SYM)))+ w("viewers/footprint.html",+ viewer("footprint",+ '<span class=h>PCB footprint</span><span class=sp></span>'+ '<button class="lyrtog on" data-lyr="copper"><span class="d" style="background:#e35d5d"></span>Copper</button>'+ '<button class="lyrtog on" data-lyr="silk"><span class="d" style="background:#e6edf3"></span>Silkscreen</button>'+ '<button class="lyrtog" data-lyr="fab"><span class="d" style="background:#8b949e"></span>Fab</button>'+ '<button class="lyrtog" data-lyr="courtyard"><span class="d" style="background:#bc8cff"></span>Courtyard</button>',+ footprint_svg(), "%s/%s footprint" % (OWNER.upper(), SYM)))
tools/gen_step.pyadded+342@@ -0,0 +1,342 @@+#!/usr/bin/env python3+"""+Emit an AP214 STEP model of the ams OSRAM SIDELED(R) package (LW A6SG).++Every dimension below is cited to "LW A6SG binning FK0PN0", version 1.1,+2020-06-01 -- page numbers in the comments. Anything the datasheet does not+dimension is marked REPRESENTATIVE and is called out in the README too.++Frame: X right, Y front(-)/back(+), Z up. Board top plane at Z = 0, so the+model drops straight onto the footprint origin.+"""+import math++# ---------------------------------------------------------------- dimensions+BODY_W = 4.00 # p.14 top view, 4 +/-0.2+BODY_H = 3.97 # p.14 front view, 3.97 +0.23/-0.17+BODY_D = 2.85 # p.14 side view, (2.85)+ENVELOPE_D = 3.63 # p.14 top view, 3.63 +/-0.2 (body + lens dome)+STANDOFF = 0.80 # p.14 side view, (0.8) -- body underside above board+LENS_DIA = 2.40 # p.14 front view, (2.4)+OPTIC_Z_REL = 2.52 # p.14 front view, 2.52 +0.28/-0.12 above body underside+LEAD_W = 1.00 # p.14 front view, 1 +/-0.1+LEAD_PITCH = 2.54 # p.14 front view, "2.54 spacing"+PAD_PITCH = 2.50 # p.15 recommended solder pad: 3.7 outer span - 1.2 pad+PAD_LEN = 3.00 # p.15 recommended solder pad, 3.0 (0.118)++LEAD_T = 0.20 # REPRESENTATIVE: lead frame stock thickness+CHAMFER = 0.50 # REPRESENTATIVE: size of the cathode-marking chamfer+FOOT_FRONT = -1.00 # REPRESENTATIVE: foot stops short of the lens face+FOOT_BACK = 1.50 # foot back edge, flush with the 3.0 mm land++Y_FRONT = -ENVELOPE_D / 2.0 # -1.815, lens tip+Y_FACE = Y_FRONT + (ENVELOPE_D - BODY_D) # -1.035, body front face+Y_BACK = Y_FRONT + ENVELOPE_D # +1.815, body back face+Z_BOT = STANDOFF # 0.80+Z_TOP = STANDOFF + BODY_H # 4.77+OPTIC_Z = STANDOFF + OPTIC_Z_REL # 3.32+DOME_H = ENVELOPE_D - BODY_D # 0.78 protrusion+DOME_R = ((LENS_DIA / 2.0) ** 2 + DOME_H ** 2) / (2.0 * DOME_H) # fitted cap++COL_BODY = (0.930, 0.930, 0.915) # white SMT housing (p.1 "white SMT package")+COL_LEAD = (0.752, 0.762, 0.780) # tin-plated lead frame+COL_LENS = (0.980, 0.958, 0.862) # "colored diffused silicone resin" (p.1)+++# ------------------------------------------------------------------- meshing+def tri_normal(a, b, c):+ u = (b[0] - a[0], b[1] - a[1], b[2] - a[2])+ v = (c[0] - a[0], c[1] - a[1], c[2] - a[2])+ return (u[1] * v[2] - u[2] * v[1],+ u[2] * v[0] - u[0] * v[2],+ u[0] * v[1] - u[1] * v[0])+++def quad(a, b, c, d):+ """Two triangles, winding preserved."""+ return [(a, b, c), (a, c, d)]+++def ear_clip(poly):+ """Triangulate a simple CCW polygon of 2D points; yields index triples."""+ idx = list(range(len(poly)))++ def area2(i, j, k):+ (ax, ay), (bx, by), (cx, cy) = poly[i], poly[j], poly[k]+ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax)++ def inside(i, j, k, p):+ d1, d2, d3 = area2(i, j, p), area2(j, k, p), area2(k, i, p)+ neg = d1 < 0 or d2 < 0 or d3 < 0+ pos = d1 > 0 or d2 > 0 or d3 > 0+ return not (neg and pos)++ out = []+ guard = 0+ while len(idx) > 3 and guard < 5000:+ guard += 1+ for n in range(len(idx)):+ i, j, k = idx[n - 1], idx[n], idx[(n + 1) % len(idx)]+ if area2(i, j, k) <= 1e-12:+ continue+ if any(inside(i, j, k, m) for m in idx if m not in (i, j, k)):+ continue+ out.append((i, j, k))+ idx.pop(n)+ break+ else:+ break+ if len(idx) == 3:+ out.append(tuple(idx))+ return out+++def extrude(profile, lo, hi, axis):+ """Extrude a CCW 2D profile between two planes normal to `axis`."""+ def place(p, t):+ if axis == 'x':+ return (t, p[0], p[1])+ if axis == 'y':+ return (p[0], t, p[1])+ return (p[0], p[1], t)++ tris, n = [], len(profile)+ for a, b, c in ear_clip(profile):+ tris.append((place(profile[c], hi), place(profile[b], hi), place(profile[a], hi)))+ tris.append((place(profile[a], lo), place(profile[b], lo), place(profile[c], lo)))+ for i in range(n):+ p, q = profile[i], profile[(i + 1) % n]+ tris += quad(place(p, lo), place(q, lo), place(q, hi), place(p, hi))+ # Extruding a CCW profile along -axis flips every face; fix in one place.+ probe = sum(tri_normal(*t)[{'x': 0, 'y': 1, 'z': 2}[axis]] for t in tris[:2])+ if probe < 0:+ tris = [(t[0], t[2], t[1]) for t in tris]+ return tris+++def box(x0, x1, y0, y1, z0, z1):+ return extrude([(x0, y0), (x1, y0), (x1, y1), (x0, y1)], z0, z1, 'z')+++def dome(cx, cz, y_base, radius, height, segs=32, rings=8):+ """Spherical cap opening toward -Y, closed with a flat base disc."""+ base_r = math.sqrt(max(radius * radius - (radius - height) ** 2, 0.0))+ y_c = y_base + radius - height # sphere centre, behind the face+ # polar angle 0 = tip (-Y), grows toward the rim+ a_rim = math.acos(max(-1.0, min(1.0, (y_c - y_base) / radius)))++ def surf(ring, seg):+ a = a_rim * ring / rings+ phi = 2.0 * math.pi * seg / segs+ r = radius * math.sin(a)+ return (cx + r * math.cos(phi), y_c - radius * math.cos(a), cz + r * math.sin(phi))++ tris = []+ tip = (cx, y_c - radius, cz)+ for s in range(segs):+ tris.append((tip, surf(1, s + 1), surf(1, s)))+ for ring in range(1, rings):+ for s in range(segs):+ tris += quad(surf(ring, s), surf(ring, s + 1),+ surf(ring + 1, s + 1), surf(ring + 1, s))+ centre = (cx, y_base, cz)+ for s in range(segs):+ p = (cx + base_r * math.cos(2 * math.pi * s / segs), y_base,+ cz + base_r * math.sin(2 * math.pi * s / segs))+ q = (cx + base_r * math.cos(2 * math.pi * (s + 1) / segs), y_base,+ cz + base_r * math.sin(2 * math.pi * (s + 1) / segs))+ tris.append((centre, p, q))+ return tris+++# ------------------------------------------------------------------ the part+def build():+ # Housing: rectangular prism with one vertical edge chamfered on the+ # cathode (-X) side -- the datasheet's "Cathode marking" callout (p.14).+ housing = [+ (-BODY_W / 2, Y_FACE + CHAMFER),+ (-BODY_W / 2 + CHAMFER, Y_FACE),+ (BODY_W / 2, Y_FACE),+ (BODY_W / 2, Y_BACK),+ (-BODY_W / 2, Y_BACK),+ ]+ solids = [('housing', COL_BODY, extrude(housing, Z_BOT, Z_TOP, 'z'))]++ # Gull-wing leads: foot on the land pattern, riser up into the housing.+ lead = [+ (FOOT_FRONT, 0.0), (FOOT_BACK, 0.0), (FOOT_BACK, Z_BOT + 0.2),+ (FOOT_BACK - LEAD_T, Z_BOT + 0.2), (FOOT_BACK - LEAD_T, LEAD_T),+ (FOOT_FRONT, LEAD_T),+ ]+ for sign, name in ((-1, 'lead_cathode'), (1, 'lead_anode')):+ cx = sign * PAD_PITCH / 2.0+ solids.append((name, COL_LEAD,+ extrude(lead, cx - LEAD_W / 2, cx + LEAD_W / 2, 'x')))++ solids.append(('lens', COL_LENS,+ dome(0.0, OPTIC_Z, Y_FACE, DOME_R, DOME_H)))++ # KiCad inverts Y between footprint space and 3D-model space, so mirror the+ # part in Y here: the lens then lands on the footprint's -Y edge, where the+ # .kicad_mod draws the emission arrow. Mirroring flips handedness, so the+ # winding of every triangle is reversed to keep normals pointing outward.+ return [(name, colour, [(mirror(t[0]), mirror(t[2]), mirror(t[1]))+ for t in tris])+ for name, colour, tris in solids]+++def mirror(p):+ return (p[0], -p[1], p[2])+++# --------------------------------------------------------------- STEP writer+class Step:+ def __init__(self):+ self.lines, self.n = [], 0++ def add(self, body):+ self.n += 1+ self.lines.append("#%d = %s;" % (self.n, body))+ return self.n++ def pt(self, p):+ return self.add("CARTESIAN_POINT('',(%s))" % ",".join("%.6f" % v for v in p))++ def dr(self, d):+ return self.add("DIRECTION('',(%s))" % ",".join("%.6f" % v for v in d))++ def ax2(self, origin, axis, ref):+ return self.add("AXIS2_PLACEMENT_3D('',#%d,#%d,#%d)"+ % (self.pt(origin), self.dr(axis), self.dr(ref)))+++def norm(v):+ m = math.sqrt(sum(c * c for c in v))+ return tuple(c / m for c in v) if m > 1e-12 else (0.0, 0.0, 1.0)+++def solid_brep(s, tris, label):+ """Triangles -> MANIFOLD_SOLID_BREP with shared vertices and edges."""+ vmap, verts = {}, []+ for t in tris:+ for p in t:+ key = tuple(round(c, 6) for c in p)+ if key not in vmap:+ vmap[key] = len(verts)+ verts.append(key)++ vpoint = [s.add("VERTEX_POINT('',#%d)" % s.pt(v)) for v in verts]++ edges = {}++ def edge(i, j):+ key = (i, j) if i < j else (j, i)+ if key not in edges:+ a, b = verts[key[0]], verts[key[1]]+ d = norm((b[0] - a[0], b[1] - a[1], b[2] - a[2]))+ vec = s.add("VECTOR('',#%d,1.000000)" % s.dr(d))+ line = s.add("LINE('',#%d,#%d)" % (s.pt(a), vec))+ edges[key] = s.add("EDGE_CURVE('',#%d,#%d,#%d,.T.)"+ % (vpoint[key[0]], vpoint[key[1]], line))+ return edges[key], (i < j)++ faces = []+ for t in tris:+ idx = [vmap[tuple(round(c, 6) for c in p)] for p in t]+ if len(set(idx)) < 3:+ continue+ n = tri_normal(*[verts[i] for i in idx])+ if math.sqrt(sum(c * c for c in n)) < 1e-12:+ continue+ n = norm(n)+ a, b = verts[idx[0]], verts[idx[1]]+ ref = norm((b[0] - a[0], b[1] - a[1], b[2] - a[2]))+ # Gram-Schmidt so the reference direction is exactly in-plane.+ dot = sum(ref[i] * n[i] for i in range(3))+ ref = norm(tuple(ref[i] - dot * n[i] for i in range(3)))+ plane = s.add("PLANE('',#%d)" % s.ax2(a, n, ref))++ oriented = []+ for k in range(3):+ ec, fwd = edge(idx[k], idx[(k + 1) % 3])+ oriented.append(s.add("ORIENTED_EDGE('',*,*,#%d,%s)"+ % (ec, ".T." if fwd else ".F.")))+ loop = s.add("EDGE_LOOP('',(%s))" % ",".join("#%d" % o for o in oriented))+ bound = s.add("FACE_OUTER_BOUND('',#%d,.T.)" % loop)+ faces.append(s.add("ADVANCED_FACE('',(#%d),#%d,.T.)" % (bound, plane)))++ shell = s.add("CLOSED_SHELL('',(%s))" % ",".join("#%d" % f for f in faces))+ return s.add("MANIFOLD_SOLID_BREP('%s',#%d)" % (label, shell))+++def write(path, solids):+ s = Step()+ ctx_axis = s.ax2((0.0, 0.0, 0.0), (0.0, 0.0, 1.0), (1.0, 0.0, 0.0))++ length = s.add("( LENGTH_UNIT() NAMED_UNIT(*) SI_UNIT(.MILLI.,.METRE.) )")+ angle = s.add("( NAMED_UNIT(*) PLANE_ANGLE_UNIT() SI_UNIT($,.RADIAN.) )")+ solid_a = s.add("( NAMED_UNIT(*) SI_UNIT($,.STERADIAN.) SOLID_ANGLE_UNIT() )")+ tol = s.add("UNCERTAINTY_MEASURE_WITH_UNIT(LENGTH_MEASURE(1.E-07),#%d,"+ "'distance_accuracy_value','confusion accuracy')" % length)+ ctx = s.add("( GEOMETRIC_REPRESENTATION_CONTEXT(3) "+ "GLOBAL_UNCERTAINTY_ASSIGNED_CONTEXT((#%d)) "+ "GLOBAL_UNIT_ASSIGNED_CONTEXT((#%d,#%d,#%d)) "+ "REPRESENTATION_CONTEXT('','3D') )" % (tol, length, angle, solid_a))++ breps, styled = [], []+ for label, colour, tris in solids:+ brep = solid_brep(s, tris, label)+ breps.append(brep)+ rgb = s.add("COLOUR_RGB('',%.6f,%.6f,%.6f)" % colour)+ fill = s.add("FILL_AREA_STYLE_COLOUR('',#%d)" % rgb)+ area = s.add("FILL_AREA_STYLE('',(#%d))" % fill)+ sfill = s.add("SURFACE_STYLE_FILL_AREA(#%d)" % area)+ side = s.add("SURFACE_SIDE_STYLE('',(#%d))" % sfill)+ usage = s.add("SURFACE_STYLE_USAGE(.BOTH.,#%d)" % side)+ psa = s.add("PRESENTATION_STYLE_ASSIGNMENT((#%d))" % usage)+ styled.append(s.add("STYLED_ITEM('colour',(#%d),#%d)" % (psa, brep)))++ shape = s.add("ADVANCED_BREP_SHAPE_REPRESENTATION('%s',(#%d,%s),#%d)"+ % ('LW_A6SG', ctx_axis,+ ",".join("#%d" % b for b in breps), ctx))+ s.add("MECHANICAL_DESIGN_GEOMETRIC_PRESENTATION_REPRESENTATION('',(%s),#%d)"+ % (",".join("#%d" % x for x in styled), ctx))++ app = s.add("APPLICATION_CONTEXT('core data for automotive mechanical "+ "design processes')")+ s.add("APPLICATION_PROTOCOL_DEFINITION('international standard',"+ "'automotive_design',2000,#%d)" % app)+ pctx = s.add("PRODUCT_CONTEXT('',#%d,'mechanical')" % app)+ prod = s.add("PRODUCT('LW_A6SG-V2BA-FK0PN0','LW A6SG-V2BA-FK0PN0',"+ "'ams OSRAM SIDELED white LED',(#%d))" % pctx)+ pdf = s.add("PRODUCT_DEFINITION_FORMATION('','',#%d)" % prod)+ pdctx = s.add("PRODUCT_DEFINITION_CONTEXT('part definition',#%d,'design')" % app)+ pdef = s.add("PRODUCT_DEFINITION('design','',#%d,#%d)" % (pdf, pdctx))+ pds = s.add("PRODUCT_DEFINITION_SHAPE('','',#%d)" % pdef)+ s.add("SHAPE_DEFINITION_REPRESENTATION(#%d,#%d)" % (pds, shape))++ header = (+ "ISO-10303-21;\n"+ "HEADER;\n"+ "FILE_DESCRIPTION(('ams OSRAM SIDELED LW A6SG-V2BA-FK0PN0'),'2;1');\n"+ "FILE_NAME('lw-a6sg-v2ba-fk0pn0.step','2026-08-27T00:00:00',"+ "('Adom'),('Adom'),'gen_step.py','Adom component pipeline','');\n"+ "FILE_SCHEMA(('AUTOMOTIVE_DESIGN { 1 0 10303 214 1 1 1 1 }'));\n"+ "ENDSEC;\n"+ "DATA;\n")+ with open(path, 'w') as fh:+ fh.write(header)+ fh.write("\n".join(s.lines))+ fh.write("\nENDSEC;\nEND-ISO-10303-21;\n")+ return s.n+++if __name__ == '__main__':+ import sys+ out = sys.argv[1] if len(sys.argv) > 1 else 'lw-a6sg-v2ba-fk0pn0.step'+ solids = build()+ n = write(out, solids)+ print("OK: %s (%d solids, %d triangles, %d entities)"+ % (out, len(solids), sum(len(t) for _, _, t in solids), n))+ print(" envelope X %.2f Y %.2f Z %.2f dome R %.3f"+ % (BODY_W, ENVELOPE_D, Z_TOP, DOME_R))
tools/hero.htmladded+50@@ -0,0 +1,50 @@+<!doctype html><html><head><meta charset=utf-8>+<link rel="preconnect" href="https://fonts.googleapis.com">+<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>+<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;700;800&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">+<style>+*{margin:0;padding:0;box-sizing:border-box}+html,body{width:4000px;height:2500px;overflow:hidden}+body{+ background:#0c1016;+ font-family:'Plus Jakarta Sans','Liberation Sans',system-ui,sans-serif;+ -webkit-font-smoothing:antialiased;+}+/* teal glow, right of centre — matches the sibling component heroes */+.orb{position:absolute;right:-620px;top:-520px;width:3100px;height:3100px;border-radius:50%;+ background:radial-gradient(circle at 50% 50%,#16252b 0%,#111d23 38%,#0d151b 62%,#0c1016 78%);}+.bar{position:absolute;left:0;top:0;width:4000px;height:24px;+ background:linear-gradient(90deg,#05e1df 0%,#34bcfc 52%,#8273f7 100%);}+.wrap{position:absolute;left:260px;top:0;width:3480px;height:2500px}+.eyebrow{position:absolute;top:296px;font-size:72px;font-weight:700;letter-spacing:.30em;+ color:#00e6dc;text-transform:uppercase;line-height:1}+#title{position:absolute;top:540px;font-weight:800;color:#eef3f8;line-height:1.0;+ letter-spacing:-.022em;white-space:nowrap}+#sub{position:absolute;font-size:118px;font-weight:500;color:#c4cdd8;line-height:1.14;+ letter-spacing:-.012em;max-width:3400px}+#sub2{position:absolute;font-size:80px;font-weight:400;color:#8b949e;line-height:1.2;+ letter-spacing:-.004em}+.footer{position:absolute;top:2286px;font-family:'JetBrains Mono','Liberation Mono',ui-monospace,monospace;+ font-size:58px;font-weight:400;color:#4d5866;letter-spacing:.06em}+</style></head><body>+<div class=orb></div><div class=bar></div>+<div class=wrap>+ <div class=eyebrow>Component</div>+ <div id=title>LW A6SG-V2BA-FK0PN0</div>+ <div id=sub>Standard LEDs - SMD, White SIDELED®</div>+ <div id=sub2>ams OSRAM · SIDELED® (side-emitting, 2-lead)</div>+ <div class=footer>Manufacturer datasheet · Datasheet-derived CAD · Adom Wiki</div>+</div>+<script>+// Fit the MPN to the 3480px column, then stack the two subtitle lines under it+// at the same rhythm the sibling component heroes use.+var t=document.getElementById('title'), s=document.getElementById('sub'), s2=document.getElementById('sub2');+var size=390;+t.style.fontSize=size+'px';+while(t.getBoundingClientRect().width>3400 && size>120){ size-=4; t.style.fontSize=size+'px'; }+var tb=t.getBoundingClientRect();+s.style.top = Math.round(tb.bottom+150)+'px';+var sb=s.getBoundingClientRect();+s2.style.top = Math.round(sb.bottom+68)+'px';+document.title='ready:'+size;+</script></body></html>
tools/render_hero.mjsadded+15@@ -0,0 +1,15 @@+import puppeteer from '/home/adom/project/adom_modules/adom/hero-studio/node_modules/puppeteer-core/lib/esm/puppeteer/puppeteer-core.js';+const CHROME='/home/adom/.cache/puppeteer/chrome/linux-152.0.7977.54/chrome-linux64/chrome';+const [,,src,out]=process.argv;+const b=await puppeteer.launch({executablePath:CHROME,headless:true,+ args:['--no-sandbox','--disable-dev-shm-usage','--font-render-hinting=none','--force-color-profile=srgb']});+const p=await b.newPage();+await p.setViewport({width:4000,height:2500,deviceScaleFactor:1});+await p.goto('file://'+src,{waitUntil:'networkidle0'});+try{ await p.evaluate(()=>document.fonts.ready); }catch(e){}+await new Promise(r=>setTimeout(r,600));+console.log('fit:',await p.title());+console.log('fonts:',await p.evaluate(()=>[...document.fonts].filter(f=>f.status==='loaded').map(f=>f.family+' '+f.weight).join(', ')||'none'));+await p.screenshot({path:out,type:'png'});+await b.close();+console.log('OK:',out);
tools/shot.mjsadded+13@@ -0,0 +1,13 @@+import puppeteer from '/home/adom/project/adom_modules/adom/hero-studio/node_modules/puppeteer-core/lib/esm/puppeteer/puppeteer-core.js';+const b=await puppeteer.launch({executablePath:'/home/adom/.cache/puppeteer/chrome/linux-152.0.7977.54/chrome-linux64/chrome',headless:true,args:['--no-sandbox','--disable-dev-shm-usage']});+for(const [src,out] of [[process.argv[2],process.argv[3]],[process.argv[4],process.argv[5]]]){+ const p=await b.newPage(); const errs=[];+ p.on('pageerror',e=>errs.push(String(e)));+ await p.setViewport({width:760,height:560});+ await p.goto('file://'+src,{waitUntil:'networkidle0'});+ await new Promise(r=>setTimeout(r,400));+ await p.screenshot({path:out});+ console.log(out, errs.length?('ERRORS: '+errs.join('; ')):'clean');+ await p.close();+}+await b.close();
tools/viewer.tpl.htmladded+65@@ -0,0 +1,65 @@+<!doctype html><html lang=en><head><meta charset=utf-8>+<meta name=viewport content="width=device-width,initial-scale=1"><title>@@TITLE@@</title>+<style>+:root{--bg:#0d1117;--panel:#161b22;--edge:#30363d;--ink:#e6edf3;--mut:#8b949e;--acc:#00b8b0;--accb:#00e6dc;--hi:#6ea8fe}+*{box-sizing:border-box;margin:0}html,body{height:100%}+body{background:var(--bg);color:var(--ink);font-family:'Satoshi',system-ui,sans-serif;display:flex;flex-direction:column}+.bar{display:flex;flex-wrap:wrap;align-items:center;gap:8px;padding:8px 11px;border-bottom:1px solid var(--edge);flex:none}+.bar .h{font-size:11px;text-transform:uppercase;letter-spacing:.06em;color:var(--mut);font-weight:700}+.bar .sp{flex:1}+.lyrtog{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:var(--mut);background:#1a2130;border:1px solid var(--edge);border-radius:999px;padding:3px 9px;cursor:pointer;user-select:none}+.lyrtog.on{color:var(--ink);border-color:#485162}.lyrtog:not(.on){opacity:.5}+.lyrtog .d{width:8px;height:8px;border-radius:50%;background:#485162}+.stage{flex:1;min-height:0;position:relative;overflow:hidden;background:var(--bg);cursor:grab;touch-action:none}+.stage.drag{cursor:grabbing}+.stage svg{position:absolute;inset:0;width:100%;height:100%}+.pingroup,.pad{cursor:pointer}+.pingroup.hi .pinvis{stroke:var(--hi)!important;stroke-width:7}+rect.pad.hi{stroke:var(--hi);stroke-width:.15;paint-order:stroke}+.lyr-off{display:none}+#tip{position:fixed;z-index:99;pointer-events:none;display:none;max-width:250px;background:#0d1117;border:1px solid var(--acc);border-radius:8px;padding:7px 10px;box-shadow:0 8px 26px #000a}+#tip .tn{font:700 12px ui-monospace,monospace;color:var(--accb)}+#tip .tt{font-size:10px;text-transform:uppercase;letter-spacing:.04em;color:var(--mut);margin-top:1px}+#tip .td{font-size:11px;color:var(--ink);margin-top:5px;line-height:1.4}+</style></head><body>+<div class=bar>@@BAR@@</div>+<div class=stage id=stage>@@SVG@@</div>+<div id=tip></div>+<script>+var KIND="@@KIND@@", MPN="@@MPN@@", PINS=@@PINS@@;+var byNum={}; PINS.forEach(function(p){byNum[String(p.number)]=p;});+var stage=document.getElementById('stage'), svg=stage.querySelector('svg'), tip=document.getElementById('tip');+// ---- pan/zoom ----+var vb=(svg.getAttribute('viewBox')||'0 0 100 100').split(/\s+/).map(Number), base=vb.slice();+function setVB(){svg.setAttribute('viewBox',vb.join(' '));}+stage.addEventListener('wheel',function(e){e.preventDefault();var r=stage.getBoundingClientRect();var mx=(e.clientX-r.left)/r.width,my=(e.clientY-r.top)/r.height;var f=e.deltaY<0?0.88:1.14;var nw=vb[2]*f,nh=vb[3]*f;vb[0]+=(vb[2]-nw)*mx;vb[1]+=(vb[3]-nh)*my;vb[2]=nw;vb[3]=nh;setVB();},{passive:false});+var pan=null;+stage.addEventListener('pointerdown',function(e){pan={x:e.clientX,y:e.clientY};stage.classList.add('drag');stage.setPointerCapture(e.pointerId);});+stage.addEventListener('pointermove',function(e){if(!pan)return;var r=stage.getBoundingClientRect();vb[0]-=(e.clientX-pan.x)/r.width*vb[2];vb[1]-=(e.clientY-pan.y)/r.height*vb[3];pan={x:e.clientX,y:e.clientY};setVB();});+function endPan(){pan=null;stage.classList.remove('drag');}+stage.addEventListener('pointerup',endPan);stage.addEventListener('pointerleave',endPan);+stage.addEventListener('dblclick',function(){vb=base.slice();setVB();});+// ---- cross-highlight bus ----+function els(n){return [].slice.call(document.querySelectorAll('[data-num="'+n+'"]'));}+function clearHi(){[].slice.call(document.querySelectorAll('.hi')).forEach(function(e){e.classList.remove('hi');});}+function hi(n){els(n).forEach(function(e){e.classList.add('hi');});}+function post(n){try{parent.postMessage({type:'lbr-hover',pin:n===null?null:String(n),mpn:MPN},'*');}catch(_){}}+function showTip(n,x,y){var p=byNum[String(n)];if(!p)return;tip.innerHTML='<span class=tn></span><div class=tt></div><div class=td></div>';tip.querySelector('.tn').textContent=n+' '+(p.name||'');tip.querySelector('.tt').textContent=p.type||'';var d=p.desc||'';if(d)tip.querySelector('.td').textContent=d;else tip.querySelector('.td').style.display='none';tip.style.display='block';tip.style.left=(x+14)+'px';tip.style.top=(y+12)+'px';}+[].slice.call(document.querySelectorAll('.pingroup,.pad')).forEach(function(g){+ var n=g.getAttribute('data-num');+ g.addEventListener('mouseenter',function(e){clearHi();hi(n);post(n);showTip(n,e.clientX,e.clientY);});+ g.addEventListener('mousemove',function(e){tip.style.left=(e.clientX+14)+'px';tip.style.top=(e.clientY+12)+'px';});+ g.addEventListener('mouseleave',function(){clearHi();post(null);tip.style.display='none';});+});+window.addEventListener('message',function(e){var d=e.data||{};if(d.type!=='lbr-hover')return;clearHi();if(d.pin!=null)hi(d.pin);});+// ---- footprint layer toggles ----+[].slice.call(document.querySelectorAll('.lyrtog')).forEach(function(b){+ function apply(on){[].slice.call(document.querySelectorAll('.ly-'+b.getAttribute('data-lyr'))).forEach(function(el){el.classList.toggle('lyr-off',!on);});}+ apply(b.classList.contains('on'));+ b.addEventListener('click',function(){apply(b.classList.toggle('on'));});+});+// ---- auto-grow the host readme iframe ----+function ph(){var h=Math.max(360,Math.min(16000,document.body.scrollHeight||420));parent.postMessage({type:'readme-height',height:h},'*');}+window.addEventListener('load',ph);setTimeout(ph,300);+</script>+</body></html>