"""D-sub connector, standard density, 9, 15, 25 and 37 positions, male (pins) or female (sockets), vertical or right-angle through-hole: NorComp 171 (vertical, dip solder) and 182 (right angle, .318" footprint). Pin 1 is at the origin; the first row (the longer one, on the D's wide side) runs along +X on a male part and along -X on a female part, so the two mate pin for pin; the second row is 2.84 along -Y. Vertical: the board is the XY plane, the D faces up (+Z), the tails go down through the board. Right-angle: the flange stands on the board with the D facing -Y, the D's wide side up; the first row of tails is the one farther from the flange. Dimensions in the README.""" from steve import Model import math positions = 9 # Positions: 9, 15, 25 or 37 gender = "male" # male (pins, plug) or female (sockets, receptacle) mounting = "vertical" # vertical (NorComp 171) or right-angle (NorComp 182, .318") detail = "full" # full: shell, insulator and contacts; simple: the metal shell only # NorComp, drawing 171-YYY-113RYY1 rev 18 (male, dip solder) and 171-YYY-213RYY1 rev 17 (female), sheet 1, table "DIMENSIONS (in/mm)", mm: # positions: (A flange width, B mounting hole spacing, C D-shell width male, C female, D rear shell width, E D-shell height male, E female, # F flange height). The 50-position size (three rows) isn't offered. VERTICAL = { 9: (30.80, 24.99, 16.92, 16.30, 19.20, 8.36, 7.90, 12.60), 15: (39.15, 33.32, 25.25, 24.65, 27.70, 8.36, 7.90, 12.60), 25: (53.00, 47.04, 38.96, 38.40, 41.10, 8.36, 7.90, 12.60), 37: (69.30, 63.50, 55.42, 54.80, 57.30, 8.36, 7.90, 12.60), } # 171, sheet 1 views (mm): flange 0.90 thick; the D 6.00 in front of the flange; the rear shell 4.10 behind the flange and 10.90 high; the # insulator 5.60 max behind the flange; the tails 4.80 beyond the insulator and 4.00 beyond its standoffs (the seating plane). V_FLANGE, V_FRONT, V_REAR_SHELL, V_REAR_INSULATOR, V_REAR_HIGH, V_TAIL_PAST_INSULATOR, V_TAIL = 0.90, 6.00, 4.10, 5.60, 10.90, 4.80, 4.00 # 171 sheet 2, recommended PCB layout (inch): the 25-position first row spans 2 x 0.652 over 12 pitches (pitch 0.1087" = 2.76 mm), the rows are # 0.056 either side of the centre line (2.84 apart). V_PITCH, ROW = 0.652 / 6 * 25.4, 0.112 * 25.4 # NorComp, drawing 182-YYY-113RYY1 rev 16 (male) and 182-YYY-213RYY1 rev 18 (female), R/A .318", sheet 1, table "DIMENSIONS", mm: # positions: (A flange width male, A female, B mounting hole spacing, C D-shell width male, C female). RIGHT_ANGLE = { 9: (30.80, 30.81, 24.99, 16.92, 16.30), 15: (39.14, 39.14, 33.32, 25.25, 24.65), 25: (53.04, 53.04, 47.04, 38.96, 38.40), 37: (69.32, 69.32, 63.50, 55.40, 54.80), } # 182 sheet 1 views (mm): flange 12.55 high, its lower edge on the board; the D 6.0 (male) or 5.9 (female) in front of the flange; overall depth # 18.51 (male) or 18.3 (female); the first row of tails 8.08 (.318") behind the flange's front face, the rows 2.84 apart, pitch 2.77; tails # 0.64 square, 3.18 below the board; Ø 3.05 holes through the flange (hardware option 43, ".120 dia. hole", sheet 2). RA_FLANGE_HIGH, RA_FRONT, RA_DEPTH, RA_ROW, RA_PITCH, RA_TAIL, RA_POST = 12.55, {"male": 6.0, "female": 5.9}, {"male": 18.51, "female": 18.3}, 8.08, 2.77, 3.18, 0.64 HOLE = 3.05 # NorComp panel cutout drawing (Panel_Cutout_D-Sub-Single): the D's sides lean 10 deg. SIDE_ANGLE = 10.0 # Proportions, for looks (README): shell sheet 0.4 thick; the D's corners rounded 0.15 x its height; the flange's corners 0.6; the male pins # Ø 1.0 rising from an insulator 1.5 above the flange to 0.6 below the D's front edge; the female insulator flush with the D's front, its # sockets Ø 1.0 and 4 deep; the vertical part's tails Ø 0.8 (the sheet's PCB hole is Ø 1.09); its rear insulator 0.6 inside the rear shell, # with four Ø 1.5 standoffs; the right-angle part's insulator body the flange's full width and height. The 171 flange (0.90) is used for the 182. SHEET, PIN, SOCKET_DEPTH, V_TAIL_D, STANDOFF_D = 0.4, 1.0, 4.0, 0.8, 1.5 if positions not in VERTICAL: raise ValueError(f"positions: one of {', '.join(map(str, VERTICAL))}") if gender not in ("male", "female") or mounting not in ("vertical", "right-angle"): raise ValueError("gender: male or female; mounting: vertical or right-angle") male = gender == "male" vertical = mounting == "vertical" first = (positions + 1) // 2 side = 1 if male else -1 if vertical: A, B, C_m, C_f, D_rear, E_m, E_f, F = VERTICAL[positions] C, E = (C_m, E_m) if male else (C_f, E_f) pitch = V_PITCH flange_front = V_TAIL_PAST_INSULATOR - V_TAIL + V_REAR_INSULATOR + V_FLANGE # seating plane to the flange's front face cw = -ROW / 2 # cross-axis (Y) centre of the D else: A_m, A_f, B, C_m, C_f = RIGHT_ANGLE[positions] A, C = (A_m, C_m) if male else (A_f, C_f) E = VERTICAL[positions][5 if male else 6] # the 182 uses the same shell; its D height is on the 171 drawing F = RA_FLANGE_HIGH pitch = RA_PITCH flange_front = RA_ROW + ROW # first row (y = 0) to the flange's front face, along -Y cw = F / 2 # cross-axis (Z) centre of the D cx = side * (first - 1) * pitch / 2 front = flange_front + (V_FRONT if vertical else RA_FRONT[gender]) flange_rear = flange_front - V_FLANGE def rounded(points, r, n=5): """A convex polygon (counter-clockwise) with its corners rounded to radius r.""" out = [] k = len(points) for i in range(k): px, py = points[i - 1] vx, vy = points[i] nx, ny = points[(i + 1) % k] a = (px - vx, py - vy) b = (nx - vx, ny - vy) la, lb = math.hypot(*a), math.hypot(*b) a, b = (a[0] / la, a[1] / la), (b[0] / lb, b[1] / lb) theta = math.acos(max(-1.0, min(1.0, a[0] * b[0] + a[1] * b[1]))) t = r / math.tan(theta / 2) bis = (a[0] + b[0], a[1] + b[1]) lbis = math.hypot(*bis) c = (vx + bis[0] / lbis * r / math.sin(theta / 2), vy + bis[1] / lbis * r / math.sin(theta / 2)) p0 = (vx + a[0] * t, vy + a[1] * t) p1 = (vx + b[0] * t, vy + b[1] * t) a0 = math.atan2(p0[1] - c[1], p0[0] - c[0]) a1 = math.atan2(p1[1] - c[1], p1[0] - c[0]) while a1 < a0: a1 += 2 * math.pi out += [(c[0] + r * math.cos(a0 + (a1 - a0) * j / n), c[1] + r * math.sin(a0 + (a1 - a0) * j / n)) for j in range(n + 1)] return out def d_shape(width, high, r, inset=0.0): """The D: `width` across its wide side (+cross axis), sides leaning SIDE_ANGLE, shrunk by `inset` all round.""" tan = math.tan(math.radians(SIDE_ANGLE)) w = width - 2 * inset / math.cos(math.radians(SIDE_ANGLE)) h = high - 2 * inset top, bottom = w / 2, w / 2 - h * tan pts = [(cx - bottom, cw - h / 2), (cx + bottom, cw - h / 2), (cx + top, cw + h / 2), (cx - top, cw + h / 2)] return rounded(pts, max(r - inset, 0.1)) def rect(w, h): return [(cx - w / 2, cw - h / 2), (cx + w / 2, cw - h / 2), (cx + w / 2, cw + h / 2), (cx - w / 2, cw + h / 2)] model = Model() def prism(fid, pts, u0, u1, name, body, op="join"): """A prism of the outline `pts` (x, cross) from u0 to u1 along the connector's axis (toward the mating face).""" if vertical: model.plane(f"{fid}_plane", on="XY", offset=u0, name=name) else: model.plane(f"{fid}_plane", on="XZ", offset=u0, name=name) # XZ at y = -u0; a positive extrude runs along -Y with model.sketch(f"{fid}_sketch", on=f"{fid}_plane", name=name) as sk: sk.polyline(f"{fid}_outline", pts, closed=True) model.extrude(fid, f"{fid}_sketch", distance=u1 - u0, op=op, body=body, name=name) def rods(fid, centres, d, u0, u1, name, body, op="new"): if vertical: model.plane(f"{fid}_plane", on="XY", offset=u0, name=name) else: model.plane(f"{fid}_plane", on="XZ", offset=u0, name=name) with model.sketch(f"{fid}_sketch", on=f"{fid}_plane", name=name) as sk: for i, (x, w) in enumerate(centres): sk.circle(f"{fid}_{i}", (x, w), d / 2) model.extrude(fid, f"{fid}_sketch", distance=u1 - u0, op=op, body=body, name=name) corner = 0.15 * E row_w = (cw + ROW / 2, cw - ROW / 2) # first and second row on the D's face face = [(side * i * pitch, row_w[0]) for i in range(first)] + [(side * (i + 0.5) * pitch, row_w[1]) for i in range(positions - first)] holes = [(cx - B / 2, cw), (cx + B / 2, cw)] # The metal shell: flange, the D in front of it and (vertical) the rear shell behind it. prism("flange", rounded(rect(A, F), 0.6), flange_rear, flange_front, "Flange", "shell_body", op="new") prism("d_shell", d_shape(C, E, corner), flange_front, front, "D shell", "shell_body") if vertical: prism("rear_shell", d_shape(D_rear, V_REAR_HIGH, corner), flange_rear - V_REAR_SHELL, flange_rear, "Rear shell", "shell_body") if detail == "full": prism("d_cavity", d_shape(C, E, corner, SHEET), flange_front, front, "D cavity", "shell_body", op="cut") rods("mount_holes", holes, HOLE, flange_rear, flange_front, "Mounting holes", "shell_body", op="cut") bodies = {"shell_body": ("steel-dc01", "#c9cdd2")} if detail == "full": # Insulator: inside the D, and behind the flange. if male: prism("insulator_face", d_shape(C, E, corner, SHEET), flange_front, flange_front + 1.5, "Insulator", "insulator_body", op="new") else: prism("insulator_face", d_shape(C, E, corner, SHEET), flange_front, front, "Insulator", "insulator_body", op="new") if vertical: floor = V_TAIL_PAST_INSULATOR - V_TAIL prism("insulator_rear", d_shape(D_rear, V_REAR_HIGH, corner, 0.6), floor, flange_rear - V_REAR_SHELL, "Rear insulator", "insulator_body") tan = math.tan(math.radians(SIDE_ANGLE)) hw, hh = (D_rear - 1.2) / 2 - (V_REAR_HIGH - 1.2) * tan - 1.6, (V_REAR_HIGH - 1.2) / 2 - 1.6 rods("standoffs", [(cx - hw, cw - hh), (cx + hw, cw - hh), (cx - hw - 2 * hh * tan, cw + hh), (cx + hw + 2 * hh * tan, cw + hh)], STANDOFF_D, 0.0, floor, "Standoffs", "insulator_body", op="join") else: back = flange_front - RA_DEPTH[gender] + RA_FRONT[gender] prism("insulator_rear", rounded(rect(A, F), 0.6), back, flange_rear, "Insulator body", "insulator_body") rods("body_holes", holes, HOLE, back, flange_rear, "Mounting holes", "insulator_body", op="cut") bodies["insulator_body"] = ("pa6", "#1d1d1f") # Contacts: male pins on the D's face, female sockets cut into the insulator; the tails below the board. if male: rods("pins", face, PIN, flange_front + 1.5, front - 0.6, "Pins", "contacts_body") else: rods("sockets", face, PIN, front - SOCKET_DEPTH, front, "Sockets", "insulator_body", op="cut") tails = [(side * i * pitch, 0.0) for i in range(first)] + [(side * (i + 0.5) * pitch, -ROW) for i in range(positions - first)] if vertical: model.plane("tails_plane", on="XY", offset=-V_TAIL, name="Tails") with model.sketch("tails_sketch", on="tails_plane", name="Tails") as sk: for i, (x, y) in enumerate(tails): sk.circle(f"tail_{i}", (x, y), V_TAIL_D / 2) model.extrude("tails", "tails_sketch", distance=V_TAIL + V_TAIL_PAST_INSULATOR - V_TAIL, op="join" if male else "new", body="contacts_body", name="Tails") else: model.plane("tails_plane", on="XY", offset=-RA_TAIL, name="Tails") with model.sketch("tails_sketch", on="tails_plane", name="Tails") as sk: for i, (x, y) in enumerate(tails): sk.rect(f"tail_{i}", (x - RA_POST / 2, y - RA_POST / 2), (x + RA_POST / 2, y + RA_POST / 2)) model.extrude("tails", "tails_sketch", distance=RA_TAIL + 1.0, op="join" if male else "new", body="contacts_body", name="Tails") bodies["contacts_body"] = ("brass", "#d4a94a") # Materials, for looks and a rough mass: the shell is nickel-plated SPCC (cold-rolled steel, DC01); the insulator PA6T / PA9T, so nylon (PA6); # the contacts gold-plated brass. for body, (material, colour) in bodies.items(): model.material(material, bodies=[body], color=colour) result = model.result()