"""SMD quartz crystal by maker series: in a ceramic package, Epson FA-238, FA-238V and TSX-3225, TXC 7M and AX, Abracon ABM8G and ABM3B, EuroQuartz MT; moulded with bent leads, Epson MA-406 and MA-505. Centred on the origin, seated on the XY plane, the long side along X. Seen from above, pad or lead 1 is at -X -Y and they count counter-clockwise (2 at +X -Y, 3 at +X +Y, 4 at -X +Y); a two-pad package has pad 1 at -X and pad 2 at +X. A ceramic package is the base with its pads underneath and a lid on top. Dimensions from each maker's data sheet, listed in the README.""" from steve import Model series = "FA-238" # Maker series detail = "full" # full: with pads or leads; simple: the body only # Each entry: (length, width, height, pad length along X, pad width along Y, gap between pads along X, gap between pads along Y, pads, # chamfered pad (0: none), chamfer, lid), lengths in mm. Lid "metal": seam-sealed metal cover; "ceramic": glass-sealed ceramic lid. # Epson "Crystal unit FA-238V / FA-238 / TSX-3225" (download.epsondevice.com/td/pdf/brief/FA-238_en.pdf, (c) 2025), "External dimensions": # FA-238: 3.2 x 2.5, 0.7 max; pads 0.9 x 0.8, 1.2 and 0.7 apart. FA-238V: the same, pad #1 chamfered. TSX-3225: 3.2 x 2.5, 0.6 max; # pads 1.0 x 0.8, 1.0 and 0.7 apart, pad #2 chamfered C 0.3 min. "#2 and #4 are connected to the cover": a metal lid. # TXC "7M MHz Crystal" Rev. 1.09 (www.txccrystal.com/images/pdf/7m-accuracy.pdf), page 2 "Dimensions": 3.20 x 2.50, 0.70 +/-0.1; # pads 1.2 and 0.7 apart, pad 1 chamfered; "Seam sealing" (page 1). # TXC catalogue page 22, "SMD Glass Sealing Crystals 8.0 x 4.5 x 1.5 mm AX Series" (copy hosted by LCSC, datasheet.lcsc.com, # product C7078706): 8.0 x 4.5, 1.50 max; two pads 2.0 wide, 5.0 apart, one chamfered. # Abracon "ABM8G" revised 08.13.15 (abracon.com/Resonators/ABM8G.pdf), page 2 "Outline drawing": 3.2 x 2.5, 1.0 max; pads 1.2 ref and # 0.9 ref apart, 0.1 ref from the ends, pad 1 chamfered; "ceramic glass sealed". # Abracon "ABM3B series" Revision U, 8/27/2024 (abracon.com/Resonators/ABM3B.pdf), page 3 "Mechanical Dimensions": 5.00 x 3.20, # 1.1 max (ABM3B); pads 1.20 x 0.80, 2.60 and 1.40 apart, pad 1 chamfered; "Seam Sealing". # EuroQuartz "MT Crystals" (www.euroquartz.co.uk/media/1904/mt.pdf), "Outline & dimensions": 3.2 x 2.5 x 0.7; pads 1.0 x 0.75, 1.2 and # 1.0 apart; "metal lid"; the chamfered pad's position varies (the sheet's note), so none is drawn. # Where a sheet gives only the gaps, the pads run from the gap to 0.1 inside the body's edge (TXC 7M both ways; ABM8G across, where the # drawing gives 0.1 ref only at the ends), a proportion read off the drawings. AX's pads run to the ends, as drawn. SERIES = { "FA-238": (3.2, 2.5, 0.7, 0.9, 0.8, 1.2, 0.7, 4, 0, 0.0, "metal"), "FA-238V": (3.2, 2.5, 0.7, 0.9, 0.8, 1.2, 0.7, 4, 1, 0.0, "metal"), "TSX-3225": (3.2, 2.5, 0.6, 1.0, 0.8, 1.0, 0.7, 4, 2, 0.3, "metal"), "7M": (3.2, 2.5, 0.7, 0.9, 0.8, 1.2, 0.7, 4, 1, 0.0, "metal"), "AX": (8.0, 4.5, 1.5, 1.5, 2.0, 5.0, 0.0, 2, 2, 0.0, "ceramic"), "ABM8G": (3.2, 2.5, 1.0, 0.9, 0.7, 1.2, 0.9, 4, 1, 0.0, "ceramic"), "ABM3B": (5.0, 3.2, 1.1, 1.2, 0.8, 2.6, 1.4, 4, 1, 0.0, "metal"), "MT": (3.2, 2.5, 0.7, 1.0, 0.75, 1.2, 1.0, 4, 0, 0.0, "metal"), } # Proportions, for looks (the sheets draw these but don't dimension them): the lid is 0.2 x the height thick (AX's ceramic lid 0.4 x, as # its side view draws it) and set in from the base's edge by 0.04 x the width; the base's corners have castellation notches of radius # 0.06 x the width; the pads are 0.05 thick, let into the base's underside; an undimensioned pad chamfer is 0.35 x the pad's smaller side. LID = 0.2 LID_AX = 0.4 PAD_T = 0.05 # Moulded packages with bent leads: (body length, body width, body height, overall height, lead centres along X, lead centres along Y, # lead width, lead foot length, feet inner span, lead ends), lengths in mm; None where the drawing doesn't give it. # Epson "Crystal unit MA-406 / MA-505 / MA-506" ((c) Seiko Epson Corporation 2020, marked discontinued; # download.epsondevice.com/td/pdf/brief/MA-406_en.pdf, the same sheet as MA-505_en.pdf), page 1 "External dimensions": # MA-406: body 11.7 max x 4.0, 3.5 high, 3.7 max overall, 4.8 max across the leads; leads 0.7 wide, 9.6 apart along the length, # on the long sides, wrapped under the body: feet (1.2) ref with 2.1 between them. # MA-505: body 12.70 x 5.08 max, 4.19 high, 4.6 max overall, 0.2 min under the body, 13.46 max over the leads; leads 1.09 wide, their # centre lines 3.3 apart across the body, on the ends, wrapped under it: feet 2.54 long. MOLDED = { "MA-406": (11.7, 4.0, 3.5, 3.7, 9.6, None, 0.7, 1.2, 2.1, None), "MA-505": (12.70, 5.08, 4.19, 4.6, None, 3.3, 1.09, 2.54, None, 13.46), } # Proportions, for looks: the leads are 0.2 thick and leave the moulding 0.4 x the overall height above the board (as the side views draw # them); the moulding's top tapers to 0.85 x its width above 0.4 x its height (the end views); MA-406's pin-1 mark is a half-round notch at # the -X end, radius 0.2 x the width and 0.1 deep; MA-505's -X top edge is bevelled 0.08 x the length. LEAD_T = 0.2 if series not in SERIES and series not in MOLDED: raise ValueError(f"no series {series!r}: {', '.join([*SERIES, *MOLDED])}") if series in SERIES: L, W, H, px, py, gx, gy, pads, chamfered, chamfer, lid = SERIES[series] lid_t = (LID_AX if series == "AX" else LID) * H base_t = H - lid_t inset = 0.04 * W notch = 0.06 * W if not chamfer: chamfer = 0.35 * min(px, py) # Pad centres, numbered as above: x and y signs. if pads == 4: places = {1: (-1, -1), 2: (1, -1), 3: (1, 1), 4: (-1, 1)} else: places = {1: (-1, 0), 2: (1, 0)} def pad_outline(n): sx, sy = places[n] x0 = sx * gx / 2 x1 = x0 + sx * px y0 = sy * gy / 2 if sy else -py / 2 y1 = y0 + (sy or 1) * py # Corners in order round the pad; the inner corner (nearest the centre) is (x0, y0) for a four-pad package. pts = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)] if n != chamfered: return pts c = chamfer if pads == 4: return [(x0 + sx * c, y0), (x1, y0), (x1, y1), (x0, y1), (x0, y0 + sy * c)] # AX: the bottom view chamfers pad 2's corner at the gap on its lower side, which is +Y seen from above (the sheets that number their # pads draw the bottom view turned over about the X axis). return [(x0, y0), (x1, y0), (x1, y1), (x0 + sx * c, y1), (x0, y1 - c)] model = Model() with model.sketch("base_sketch", on="XY", name="Base") as sk: sk.rect("base_rect", (-L / 2, -W / 2), (L / 2, W / 2)) model.extrude("base", "base_sketch", distance=base_t, op="new", body="base_body", name="Base") with model.sketch("notch_sketch", on="XY", name="Castellations") as sk: for i, (sx, sy) in enumerate(((-1, -1), (1, -1), (1, 1), (-1, 1))): sk.circle(f"notch_{i}", (sx * L / 2, sy * W / 2), notch) model.extrude("notches", "notch_sketch", distance=base_t, op="cut", body="base_body", name="Castellations") model.plane("lid_plane", on="XY", offset=base_t, name="Lid") with model.sketch("lid_sketch", on="lid_plane", name="Lid") as sk: sk.rect("lid_rect", (-L / 2 + inset, -W / 2 + inset), (L / 2 - inset, W / 2 - inset)) model.extrude("lid", "lid_sketch", distance=lid_t, op="new", body="lid_body", name="Lid") bodies = ["base_body", "lid_body"] if detail == "full": with model.sketch("pad_sketch", on="XY", name="Pads") as sk: for n in places: sk.polyline(f"pad_{n}", pad_outline(n), closed=True) model.extrude("pad_pockets", "pad_sketch", distance=PAD_T, op="cut", body="base_body", name="Pad pockets") for n in places: with model.sketch(f"pad_{n}_sketch", on="XY", name=f"Pad {n}") as sk: sk.polyline(f"pad_{n}_outline", pad_outline(n), closed=True) model.extrude(f"pad_{n}", f"pad_{n}_sketch", distance=PAD_T, op="new", body=f"pad_{n}_body", name=f"Pad {n}") model.extrude(f"pad_{n}_notches", "notch_sketch", distance=PAD_T, op="cut", body=f"pad_{n}_body", name=f"Pad {n} castellations") # Materials, for looks and a rough mass: the base is alumina ceramic; a metal lid is a Fe-Ni-Co alloy (Kovar, density about 8.4), and invar, # the Fe-Ni alloy in the material list (8.05), stands in for it; a ceramic lid is alumina too; the pads are gold-plated tungsten # metallisation, gold (density 19.3, as tungsten's). model.material("alumina", bodies=["base_body"], color="#d9cdb2") if lid == "metal": model.material("invar", bodies=["lid_body"], color="#c9ccd1") else: model.material("alumina", bodies=["lid_body"], color="#2e3033") if detail == "full": model.material("gold", bodies=[f"pad_{n}_body" for n in places], color="#d4a94a") else: BL, BW, BH, H, cx, cy, lw, foot, span, ends = MOLDED[series] z0 = H - BH rise = 0.4 * H model = Model() model.plane("body_end", on="YZ", offset=-BL / 2, name="Body") with model.sketch("body_sketch", on="body_end", name="Body") as sk: sk.polyline("body_outline", [(-BW / 2, z0), (BW / 2, z0), (BW / 2, z0 + 0.4 * BH), (0.425 * BW, H), (-0.425 * BW, H), (-BW / 2, z0 + 0.4 * BH)], closed=True) model.extrude("body", "body_sketch", distance=BL, op="new", body="body_body", name="Body") if series == "MA-406": model.plane("top_plane", on="XY", offset=H, name="Top") with model.sketch("mark_sketch", on="top_plane", name="Pin-1 mark") as sk: sk.circle("mark_circle", (-BL / 2, 0), 0.2 * BW) model.extrude("mark", "mark_sketch", distance=-0.1, op="cut", body="body_body", name="Pin-1 mark") else: b = 0.08 * BL model.plane("bevel_plane", on="XZ", offset=-(BW / 2 + 1), name="Bevel") with model.sketch("bevel_sketch", on="bevel_plane", name="Pin-1 bevel") as sk: sk.polyline("bevel_outline", [(-BL / 2 - 0.1, H - b), (-BL / 2 + b, H + 0.1), (-BL / 2 - 0.1, H + 0.1)], closed=True) model.extrude("bevel", "bevel_sketch", distance=BW + 2, op="cut", body="body_body", name="Pin-1 bevel") bodies = ["body_body"] if detail == "full": for n, (sx, sy) in {1: (-1, -1), 2: (1, -1), 3: (1, 1), 4: (-1, 1)}.items(): if series == "MA-406": # On a long side: the foot under the body from the inner span out, then up the side; sketched across Y on a YZ plane # (normal +X), extruded along X by the lead's width. u0, u1 = sy * span / 2, sy * (span / 2 + foot) plane, offset = "YZ", sx * cx / 2 - lw / 2 else: # On an end: the foot under the body from the lead end in, then up the end; sketched along X on an XZ plane (normal -Y), # extruded across Y by the lead's width. u0, u1 = sx * (ends / 2 - foot), sx * ends / 2 plane, offset = "XZ", -(sy * cy / 2 + lw / 2) s_out = 1 if u1 > u0 else -1 ub = u1 - s_out * LEAD_T model.plane(f"lead_{n}_plane", on=plane, offset=offset, name=f"Lead {n}") with model.sketch(f"lead_{n}_profile", on=f"lead_{n}_plane", name=f"Lead {n}") as sk: sk.polyline(f"lead_{n}_shape", [(u0, 0), (u1, 0), (u1, rise), (ub, rise), (ub, LEAD_T), (u0, LEAD_T)], closed=True) model.extrude(f"lead_{n}", f"lead_{n}_profile", distance=lw, op="new", body=f"lead_{n}_body", name=f"Lead {n}") bodies.append(f"lead_{n}_body") # Materials, for looks and a rough mass: epoxy moulding compound is mostly silica filler, density about 1.9 to 2.0, with the crystal's # metal can inside; ptfe (2.2) is the nearest in the material list. The leads are tinned copper alloy, copper. model.material("ptfe", bodies=["body_body"], color="#26282b") if detail == "full": model.material("copper", bodies=bodies[1:], color="#c9ccd1") result = model.result()