"""Leaded (axial) inductor, Fastron fixed choke coils (MICC, SMCC, MECC, HCCC, LACC, HBCC, VHBCC, XHBCC) and suppression coils (MISC, MESC, 77A), mounted horizontal or vertical. Lead 1 is at the origin and lead 2 at +X by the pitch; the leads go down through the board. Horizontal: the body lies on the board along X, centred between the leads. Vertical: the body stands on the board over lead 1 and lead 2 comes back down beside it. The series, inductance and tolerance make the part number (e.g. MECC-100K); the size follows from them. Dimensions from Fastron's data sheets, listed in the README.""" import math from steve import Model series = "MECC" # Series inductance = 10.0 # µH Inductance (one the series' data sheet lists) tolerance = "K" # J 5 %, K 10 %, M 20 % (the colour bands; one the sheet offers for this inductance) mounting = "horizontal" # horizontal or vertical pitch = 15.24 # mm Lead pitch on the board tail = 3.0 # mm Leads below the board detail = "full" # full: body, colour bands and leads; simple: the body only # Body sizes (length max, diameter max, lead diameter) in mm by series and inductance in µH, from Fastron's data sheets, each one page # (fastrongroup.com, product pages of the series; "Revision date" printed on each sheet): # MICC (08 Feb 2021): 7 max, Ø 3.3 max for R10 to 150 and Ø 3.0 max for 180 to 102, lead Ø 0.5. # SMCC (05 Aug 2021): 9.5 max, Ø 4.0 max, lead Ø 0.60. MECC (06 Jul 2017): 11.0 max, Ø 4.5 max, lead Ø 0.6. # HCCC (11 Aug 2014): 13 max, Ø 4.5 max, lead Ø 0.8. LACC (11 Aug 2014): 14.0 max, Ø 4.5 max, lead Ø 0.6. # HBCC (14 Apr 2025): A 14.5 max for 1.0 to 18 µH, 12.8 max for 22 to 100000 µH, Ø 5.8 max, lead Ø 0.60. # VHBCC (23 May 2017): 16.0 max, Ø D 6.3 max for 47 µH and 6.0 for 10 to 33 and 68 to 100000 µH, lead Ø 0.7. # XHBCC (31 May 2016): 16 max, Ø 7.5 max, lead Ø 0.7. # MISC (27 Apr 2017): 12.0 max overall, 10.0 max winding, Ø 5.0 max, lead Ø 0.80. MESC (13 Oct 2017): 24.0 max, Ø 7.5 max, lead Ø 0.80. # 77A (26 Jun 2025): dimension table A max, Ø D max, Ø d by part number, 3 ref at each end (Fig 1 and 2). # Each entry: (inductances in µH, length, diameter, lead diameter). SIZES = { "MICC": (((0.1, 150), 7.0, 3.3, 0.5), ((180, 1000), 7.0, 3.0, 0.5)), "SMCC": (((0.1, 10000), 9.5, 4.0, 0.6),), "MECC": (((1, 150), 11.0, 4.5, 0.6),), "HCCC": (((0.7, 33), 13.0, 4.5, 0.8),), "LACC": (((1, 56), 14.0, 4.5, 0.6),), "HBCC": (((1, 18), 14.5, 5.8, 0.6), ((22, 100000), 12.8, 5.8, 0.6)), "VHBCC": (((47, 47), 16.0, 6.3, 0.7), ((10, 33), 16.0, 6.0, 0.7), ((68, 100000), 16.0, 6.0, 0.7)), "XHBCC": (((100, 100000), 16.0, 7.5, 0.7),), "MISC": (((1, 100), 12.0, 5.0, 0.8),), "MESC": (((3, 1500), 24.0, 7.5, 0.8),), "77A": (((3.9, 3.9), 30.0, 8.0, 1.0), ((5.6, 10), 26.0, 8.0, 0.8), ((15, 15), 26.0, 9.0, 0.8), ((33, 33), 26.0, 10.0, 0.8), ((40, 10000), 26.0, 11.0, 0.8)), } # The inductances each sheet lists (µH), by the tolerances it offers for them (J 5 %, K 10 %, M 20 %): the part number table of each sheet. VALUES = { "MICC": {"KM": (0.1, 0.11, 0.12, 0.15, 0.18, 0.2, 0.22, 0.27, 0.33, 0.39, 0.47, 0.56, 0.68, 0.82), "JKM": (1, 1.2, 1.3, 1.5, 1.8, 2.2, 2.4, 2.7, 3.3, 3.9, 4, 4.4, 4.7, 4.9, 5, 5.6, 6.2, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 62, 68, 82, 100, 120, 150, 180, 220, 270, 280, 330, 350, 390, 470, 560, 680, 820, 1000)}, "SMCC": {"KM": (0.1, 0.12, 0.15, 0.18, 0.22, 0.27, 0.33, 0.39, 0.47, 0.56, 0.68, 0.82), "JKM": (1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.2, 6.8, 8.2, 10, 12, 15, 18, 20, 22, 27, 30, 33, 39, 47, 56, 68, 82, 100, 120, 150, 160, 180, 200, 220, 270, 330, 350, 390, 470, 560, 680, 820, 1000, 1200, 1500, 1800, 2200, 2700, 3300, 3500, 3900, 4700, 5000, 5600, 6800, 8100, 8200, 10000)}, "MECC": {"KM": (1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33, 39, 47, 56, 68, 82, 100, 120, 150)}, "HCCC": {"M": (0.7,), "KM": (1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.6, 3.9, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 27, 33)}, "LACC": {"KM": (1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 24, 27, 33, 39, 43, 47, 56)}, "HBCC": {"JK": (1, 1.2, 1.5, 1.8, 2.2, 2.7, 3.3, 3.9, 4.7, 5.6, 6.8, 8.2, 10, 12, 15, 18, 22, 25, 27, 33, 39, 47, 56, 68, 82, 100, 120, 150, 180, 220, 270, 330, 390, 470, 560, 680, 820, 1000, 1200, 1500, 1800, 2200, 2700, 3300, 3900, 4700, 5000, 5300, 6800, 10000, 15000, 18000, 22000, 33000, 36000, 39000, 47000, 68000, 100000)}, "VHBCC": {"JK": (10, 15, 22, 33, 47, 68, 100, 150, 180, 220, 330, 390, 470, 560, 680, 820, 1000, 1200, 1500, 1800, 2200, 2700, 3300, 3900, 4700, 5600, 6800, 8200, 10000, 12000, 15000, 18000, 22000, 27000, 33000, 47000, 68000, 100000)}, "XHBCC": {"K": (100, 150, 220, 330, 470), "JK": (680, 1000, 1500, 1700, 2000, 2200, 2400, 2500, 2700, 3000, 3300, 3500, 3600, 4500, 4700, 5300, 6800, 10000, 15000, 22000, 33000, 47000, 68000, 100000)}, "MISC": {"M": (1, 2, 3, 6, 14, 30, 40, 100)}, "MESC": {"M": (3, 3.3, 5, 7, 10, 12, 15, 22, 25, 40, 55, 56, 75, 100, 130, 160, 220, 330, 350, 470, 680, 1200, 1500)}, "77A": {"M": (3.9, 5.6, 10, 15, 33, 40, 68, 80, 100, 120, 150, 330, 470, 500, 680, 820, 1000, 1500, 3300, 4700, 5600, 6800, 10000)}, } # Suppression coils: a winding seen through a clear insulation foil, no colour bands. The foil's end length at each end: MISC (12.0 - 10.0) / 2 # from its sheet, 77A "3 ref" from its sheet. MESC's sheet doesn't dimension it: 3 is taken from 77A, the same maker's drawing style, for looks. SLEEVE_ENDS = {"MISC": 1.0, "MESC": 3.0, "77A": 3.0} # Colour code (IEC 60062) as the Fastron sheets give it: two digit bands, a multiplier band, a tolerance band (none for M, 20 %). DIGIT_COLOURS = ("#1a1a1a", "#7a4a26", "#c8302c", "#e8792b", "#f2d22e", "#3f8f3f", "#2f5fb3", "#7d4fa8", "#8a8a8a", "#f4f4f4") MULTIPLIER_COLOURS = {-2: "#c0c0c4", -1: "#cfa646", 0: "#1a1a1a", 1: "#7a4a26", 2: "#c8302c", 3: "#e8792b", 4: "#f2d22e"} TOLERANCE_COLOURS = {"J": "#cfa646", "K": "#c0c0c4"} if series not in SIZES: raise ValueError(f"no series {series!r}: {', '.join(SIZES)}") offered = [(tols, v) for tols, values in VALUES[series].items() for v in values if math.isclose(v, inductance, rel_tol=1e-6)] if not offered: raise ValueError(f"Fastron {series} has no {inductance} µH part: see the series' data sheet") if tolerance not in offered[0][0]: raise ValueError(f"Fastron {series} {inductance} µH comes in tolerance {' or '.join(offered[0][0])}") size = [s for s in SIZES[series] if s[0][0] - 1e-9 <= inductance <= s[0][1] + 1e-9] _range, length, diameter, lead = size[0] if mounting not in ("horizontal", "vertical"): raise ValueError("mounting is horizontal or vertical") R, r = diameter / 2, lead / 2 BEND = lead # bend radius of the formed leads, one lead diameter: a proportion, for looks (the sheets give none) LOOP = 2.0 # vertical: lead 2 clears the body's top end by this before it turns down, a proportion if mounting == "horizontal" and pitch < length + 2 * BEND: raise ValueError(f"horizontal pitch must leave room to bend the leads: at least {length + 2 * BEND:.2f} mm for a {length} mm body") if mounting == "vertical" and pitch < max(R + r, 2 * BEND): raise ValueError(f"vertical pitch must let lead 2 pass the body: at least {max(R + r, 2 * BEND):.2f} mm for a {diameter} mm body") sleeved = series in SLEEVE_ENDS reach = SLEEVE_ENDS.get(series, 0.0) if detail == "full" else 0.0 # the leads reach into the body this far: to the winding under the foil def body_profile(): """The body outline as (u along the axis from the body's lead-1 end, radius), and the band segments' indices.""" if sleeved: # The foil closes over the winding's ends down to the lead: a quarter-sine curve over the end length, for looks. end = SLEEVE_ENDS[series] ends = [(end * (1 - math.cos(math.pi / 2 * i / 4)), r + (R - r) * math.sin(math.pi / 2 * i / 4)) for i in range(1, 5)] return [(0, 0), (0, r)] + ends + [(length - u, v) for u, v in reversed(ends)] + [(length, r), (length, 0)], [] # Coated chokes: tapered ends from the lead out to the full diameter over 0.8 x the radius, for looks; the sheets draw the taper but don't # dimension it. Four bands, each 0.07 x the length wide, centred at 0.30, 0.42, 0.54 and 0.70 of the length: proportions, for looks. taper, neck = 0.8 * R, min(lead, 0.6 * R) pts = [(0, 0), (0, neck)] pts += [(taper * (1 - math.cos(math.pi / 2 * i / 4)), neck + (R - neck) * math.sin(math.pi / 2 * i / 4)) for i in range(1, 5)] bands = [] for centre in (0.30, 0.42, 0.54, 0.70): a, b = centre * length - 0.035 * length, centre * length + 0.035 * length pts += [(a, R), (b, R)] bands.append(len(pts) - 2) pts += [(length - u, v) for u, v in reversed(pts[2:6])] pts += [(length, neck), (length, 0)] return pts, bands def band_colours(): exponent = math.floor(math.log10(inductance)) - 1 digits = round(inductance / 10 ** exponent) if digits >= 100: digits, exponent = digits // 10, exponent + 1 colours = [DIGIT_COLOURS[digits // 10], DIGIT_COLOURS[digits % 10], MULTIPLIER_COLOURS[exponent]] if tolerance in TOLERANCE_COLOURS: colours.append(TOLERANCE_COLOURS[tolerance]) return colours model = Model() outline, bands = body_profile() horizontal = mounting == "horizontal" x0 = pitch / 2 - length / 2 axis_z = R # horizontal: the body rests on the board if horizontal: points = [(x0 + u, axis_z + v) for u, v in outline] axis = ((0, 0, axis_z), (1, 0, 0)) else: points = [(v, u) for u, v in outline] axis = "Z" with model.sketch("body_sketch", on="XZ", name="Body") as sk: sk.polyline("body_outline", points, closed=True) model.revolve("body", "body_sketch", axis=axis, op="new", body="body_body", name="Body") bodies = ["body_body"] if detail == "full" and sleeved: # The winding under the foil: turns drawn 0.5 mm apart, 0.1 deep (for looks: the sheets give no wire size), filling the foil's straight part. end = SLEEVE_ENDS[series] foil = 0.2 # foil thickness, for looks rw = R - foil turns = max(4, int((length - 2 * end) / 0.5)) step = (length - 2 * end) / turns wind = [(end, 0), (end, rw - 0.1)] for i in range(turns): wind += [(end + (i + 0.5) * step, rw), (end + (i + 1) * step, rw - 0.1)] wind += [(length - end, 0)] wpoints = [(x0 + u, axis_z + v) for u, v in wind] if horizontal else [(v, u) for u, v in wind] # The foil becomes a shell over the winding: the body is cut by the winding's outer envelope and by the leads' way in through its ends, # and the winding added as its own body. inside = ((0, 0), (0, r), (end, r), (end, rw), (length - end, rw), (length - end, r), (length, r), (length, 0)) with model.sketch("core_sketch", on="XZ", name="Winding") as sk: sk.polyline("core_outline", [(x0 + u, axis_z + v) for u, v in inside] if horizontal else [(v, u) for u, v in inside], closed=True) model.revolve("core_cut", "core_sketch", axis=axis, op="cut", body="body_body", name="Foil inside") with model.sketch("winding_sketch", on="XZ", name="Winding") as sk: sk.polyline("winding_outline", wpoints, closed=True) model.revolve("winding", "winding_sketch", axis=axis, op="new", body="winding_body", name="Winding") bodies.append("winding_body") if detail == "full": # Leads, swept along their bends. Horizontal: up from below the board at x = 0 and x = pitch to the body's axis, bent toward the body. # Vertical: lead 1 straight down from the body's bottom end; lead 2 up from the top end, over and down at x = pitch. if horizontal: legs = [(0.0, 1, x0 + reach), (pitch, -1, x0 + length - reach)] for i, (x, side, x_end) in enumerate(legs, 1): with model.sketch(f"lead_{i}_path", on="XZ", name=f"Lead {i} path") as sk: sk.line(f"lead_{i}_up", (x, -tail), (x, axis_z - BEND), construction=True) if side > 0: sk.arc(f"lead_{i}_bend", (x + BEND, axis_z - BEND), BEND, 90, 180, construction=True) else: sk.arc(f"lead_{i}_bend", (x - BEND, axis_z - BEND), BEND, 0, 90, construction=True) sk.line(f"lead_{i}_along", (x + side * BEND, axis_z), (x_end, axis_z), construction=True) model.plane(f"lead_{i}_start", on="XY", offset=-tail, name=f"Lead {i} end") with model.sketch(f"lead_{i}_section", on=f"lead_{i}_start", name=f"Lead {i}") as sk: sk.circle(f"lead_{i}_round", (x, 0), r) model.sweep(f"lead_{i}", f"lead_{i}_section", f"lead_{i}_path", op="new", body=f"lead_{i}_body", name=f"Lead {i}") bodies.append(f"lead_{i}_body") else: model.plane("lead_1_start", on="XY", offset=-tail, name="Lead 1 end") with model.sketch("lead_1_section", on="lead_1_start", name="Lead 1") as sk: sk.circle("lead_1_round", (0, 0), r) model.extrude("lead_1", "lead_1_section", distance=tail + reach, op="new", body="lead_1_body", name="Lead 1") top = length + LOOP with model.sketch("lead_2_path", on="XZ", name="Lead 2 path") as sk: sk.line("lead_2_up", (0, length - reach), (0, top - BEND), construction=True) sk.arc("lead_2_bend_1", (BEND, top - BEND), BEND, 90, 180, construction=True) sk.line("lead_2_over", (BEND, top), (pitch - BEND, top), construction=True) sk.arc("lead_2_bend_2", (pitch - BEND, top - BEND), BEND, 0, 90, construction=True) sk.line("lead_2_down", (pitch, top - BEND), (pitch, -tail), construction=True) model.plane("lead_2_start", on="XY", offset=length - reach, name="Lead 2 start") with model.sketch("lead_2_section", on="lead_2_start", name="Lead 2") as sk: sk.circle("lead_2_round", (0, 0), r) model.sweep("lead_2", "lead_2_section", "lead_2_path", op="new", body="lead_2_body", name="Lead 2") bodies += ["lead_1_body", "lead_2_body"] # Materials, for looks and a rough mass: the coating and the foil as a plastic (the sheets name only the core: ferrite or iron dust), the # winding copper, the leads tinned copper. model.material("pla", bodies=["body_body"], color="#e9e4d6" if sleeved else "#dcc196") if sleeved: model.appearance(color="#e9e4d6", opacity=0.45, bodies=["body_body"]) if detail == "full": if sleeved: model.material("copper", bodies=["winding_body"], color="#c8834f") model.material("copper", bodies=[b for b in bodies if b.startswith("lead")], color="#c9ccd1") if bands: for i, colour in zip(bands, band_colours()): model.appearance(color=colour, faces=[f"body.side(body_outline.side{i})"]) result = model.result()