{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "\n", "import matplotlib.pyplot as plt\n", "from skspatial.objects import LineSegment, Line, Vector\n", "\n", "# some helper functions\n", "from helpers import (\n", " get_arc_point,\n", " draw_arc,\n", " rotate,\n", " translate,\n", " flip_y,\n", " flip_x,\n", " optimize_points,\n", " chaikin,\n", " rotate_point,\n", " scale,\n", ")\n", "from pcb_json import (\n", " dump_json,\n", " plot_json,\n", " create_via,\n", " create_pad,\n", " create_pin,\n", " create_silk,\n", " create_mounting_hole,\n", ")\n", "\n", "from enum import Enum\n", "\n", "Layer = Enum(\"Layer\", \"FRONT BACK\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Track width and spacing\n", "TRACK_WIDTH = 0.102\n", "TRACK_SPACING = 0.2\n", "\n", "# via defaults\n", "VIA_DIAM = 0.8\n", "VIA_DRILL = 0.4\n", "\n", "# this is for a 1.27mm pitch pin\n", "PIN_DIAM = 1.0\n", "PIN_DRILL = 0.65\n", "\n", "# this is for the PCB connector - see https://www.farnell.com/datasheets/2003059.pdf\n", "PAD_ENABLE = False\n", "PAD_WIDTH = 3\n", "PAD_HEIGHT = 2\n", "PAD_PITCH = 2.5\n", "\n", "# Vias for connecting to the coils\n", "COIL_INPUT_VIAS_ENABLE = True\n", "\n", "# PCB edge cuts\n", "PCB_EDGE_CUTS = False\n", "\n", "# Stator hole\n", "STATOR_HOLE_RADIUS = 5.5\n", "\n", "# Coil net name\n", "COIL_NET_NAME = \"coils\"" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Standard 25 mm version\n", "\n", "# PCB Edge size\n", "STATOR_RADIUS = 25\n", "STATOR_HOLE_RADIUS = 10\n", "\n", "# where to puth the mounting pins\n", "SCREW_HOLE_DRILL_DIAM = 2.3 # 2.3mm drill for a 2mm screw\n", "SCREW_HOLE_RADIUS = STATOR_RADIUS\n", "\n", "# Coil params\n", "TURNS = 12\n", "COIL_CENTER_RADIUS = 16\n", "COIL_VIA_RADIUS = 17\n", "\n", "# Coil input vias\n", "COIL_INPUT_VIA_RADIUS = 22" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Large 35 mm version\n", "\n", "# PCB Edge size\n", "STATOR_RADIUS = 35\n", "\n", "# where to puth the mounting pins\n", "SCREW_HOLE_DRILL_DIAM = 2.3 # 2.3mm drill for a 2mm screw\n", "SCREW_HOLE_RADIUS = STATOR_RADIUS\n", "\n", "# Coil params\n", "# for custom shape\n", "TURNS = 13\n", "# for spiral\n", "# TURNS = 17\n", "COIL_CENTER_RADIUS = 20.45\n", "COIL_VIA_RADIUS = 20.95\n", "\n", "# Coil input vias\n", "COIL_INPUT_VIA_RADIUS = 27" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# LED ring\n", "LED_RING_ENABLE = True\n", "LED_RING_RADIUS = 32\n", "LED_RING_VIA_OFFSET = 2.5\n", "LED_RING_OUTER_VIA_RADIUS = LED_RING_RADIUS + LED_RING_VIA_OFFSET\n", "LED_RING_INNER_VIA_RADIUS = LED_RING_RADIUS - LED_RING_VIA_OFFSET\n", "LED_CHIP_PAD_RADIAL_FROM_CENTRE = 0.75\n", "LED_CHIP_PAD_ANGLE_FROM_CENTRE = -1\n", "LED_RING_NUM_LEDS = 60\n", "LED_PAD_ANGLE_OFFSET = 1\n", "LED_VPLUS_NET_NAME = \"V+\"\n", "LED_GND_NET_NAME = \"GND\"\n", "LED_IO_NET_NAME_BASE = \"LED_IO_\"\n", "LED_IO_NET_NC = \"\"\n", "LED_IO_NET_INPUT = \"LED_RING_INPUT\"\n", "LED_POWER_TRACK_WIDTH = 1.5\n", "LED_POWER_INTERCONN_TRACK_WIDTH = 0.5\n", "LED_INTERCONN_TO_SUPPRESS = 58\n", "LED_REF_BASE = \"LED\"\n", "LED_RING_START_LED_REF = 38\n", "LED_RING_END_LED_REF = 39" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# where to put the input pads\n", "INPUT_PAD_RADIUS = COIL_VIA_RADIUS\n", "print(INPUT_PAD_RADIUS)\n", "\n", "USE_SPIRAL = False\n", "\n", "if USE_SPIRAL:\n", " TURNS = 18\n", " COIL_VIA_RADIUS = 20.5\n", " COIL_CENTER_RADIUS = 20.5\n", "\n", "LAYERS = 8\n", "\n", "OUTER_CONN_RADIUS = 28.4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Arbitrary Coil Generation" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# templates must be simetric around the X axis and must include the center points on both size (e.g. (X1, 0).... (X2, 0) )\n", "# template must also be convex\n", "template = [\n", " (-3.5, 0),\n", " (-3.5, -0.01),\n", " (1.9, -1.45),\n", " (1.9, 0.0),\n", " (1.9, 1.45),\n", " (-3.5, 0.01),\n", "]" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# plot the template shape wrapping around to the first point\n", "df = pd.DataFrame(template + [template[0]], columns=[\"x\", \"y\"])\n", "ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\")\n", "ax.axis(\"equal\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def calculate_point(point, point1, point2, spacing, turn):\n", " reference_vector = Vector([-100, 0])\n", " angle = np.rad2deg(Vector(point).angle_between(reference_vector))\n", " if point[1] > 0:\n", " angle = 360 - angle\n", " vector = Vector(point1) - Vector(point2)\n", " normal = vector / np.linalg.norm(vector)\n", " # rotate the vector 90 degrees\n", " normal = np.array([-normal[1], normal[0]])\n", " # move the point along the normal vector by the spacing\n", " offset = spacing * (turn * 360 + angle) / 360\n", " coil_point = point + normal * offset\n", " return (coil_point[0], coil_point[1])\n", "\n", "\n", "def get_points(template, turns, spacing):\n", " coil_points = []\n", " reference_vector = Vector([-100, 0])\n", " template_index = 0\n", " template_length = len(template)\n", " for turn in range(turns * template_length):\n", " point1 = template[template_index % template_length]\n", " point2 = template[(template_index + 1) % template_length]\n", "\n", " # calculate the new positions of the points\n", " coil_point1 = calculate_point(\n", " point1, point1, point2, spacing, template_index // template_length\n", " )\n", " coil_point2 = calculate_point(\n", " point2, point1, point2, spacing, (template_index + 1) // template_length\n", " )\n", " # adjust the previous point so that the previous line intersects with this new line\n", " # this prevents any cutting of corners\n", " if len(coil_points) >= 2:\n", " # create a line from the previous two points\n", " line1 = Line(\n", " coil_points[len(coil_points) - 2],\n", " np.array(coil_points[len(coil_points) - 1])\n", " - np.array(coil_points[len(coil_points) - 2]),\n", " )\n", " # create a line from the two new points\n", " line2 = Line(\n", " np.array(coil_point1),\n", " np.array(np.array(coil_point1) - np.array(coil_point2)),\n", " )\n", " # find the intersection of the two lines\n", " try:\n", " intersection = line1.intersect_line(line2)\n", " # replace the previous point with the intersection\n", " coil_points[len(coil_points) - 1] = intersection\n", " # add the new point\n", " coil_points.append(coil_point2)\n", " except:\n", " # the lines did not intersect so just add the points\n", " coil_points.append(coil_point1)\n", " coil_points.append(coil_point2)\n", " else:\n", " coil_points.append(coil_point1)\n", " coil_points.append(coil_point2)\n", "\n", " template_index = template_index + 1\n", " return coil_points" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def get_spiral(turns, start_radius, thickness, layer=Layer.FRONT):\n", " points = []\n", " # create a starting point in the center\n", " for angle in np.arange(0, turns * 360, 1):\n", " radius = start_radius + thickness * angle / 360\n", " if layer == Layer.BACK:\n", " x = radius * np.cos(np.deg2rad(angle + 180))\n", " y = radius * np.sin(np.deg2rad(angle + 180))\n", " points.append((x, -y))\n", " else:\n", " x = radius * np.cos(np.deg2rad(angle))\n", " y = radius * np.sin(np.deg2rad(angle))\n", " points.append((x, y))\n", " return points" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if not USE_SPIRAL:\n", " print(\"Not using spiral\")\n", " template_f = []\n", " for i in range(len(template)):\n", " template_f.append(template[len(template) - i - len(template) // 2])\n", " template_f = flip_x(template_f)\n", " points_f = chaikin(\n", " optimize_points(\n", " flip_x(get_points(template_f, TURNS, TRACK_SPACING + TRACK_WIDTH))\n", " ),\n", " 2,\n", " )\n", " points_b = chaikin(\n", " optimize_points(get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH)), 2\n", " )\n", "else:\n", " print(\"Using spiral\")\n", " points_f = get_spiral(\n", " TURNS, VIA_DIAM / 2 + TRACK_SPACING, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT\n", " )\n", " points_b = get_spiral(\n", " TURNS, VIA_DIAM / 2 + TRACK_SPACING, TRACK_SPACING + TRACK_WIDTH, Layer.BACK\n", " )\n", "\n", " points_f = [(0, 0)] + points_f\n", " points_b = [(0, 0)] + points_b\n", " print(\"Track points\", len(points_f), len(points_b))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "points_f = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_f\n", "points_b = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_b\n", "\n", "df = pd.DataFrame(points_f, columns=[\"x\", \"y\"])\n", "ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\")\n", "ax.axis(\"equal\")\n", "df = pd.DataFrame(points_b, columns=[\"x\", \"y\"])\n", "ax = df.plot.line(x=\"x\", y=\"y\", color=\"red\", ax=ax)\n", "\n", "print(\"Track points\", len(points_f), len(points_b))" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# write the coil out in a format that can be simulated\n", "# rotate the points by 90 degrees so that the x axis is horizontal\n", "pf = rotate(points_f, 90)\n", "pb = rotate(points_b, 90)\n", "fname = \"simulations/coils/coil_12_custom\"\n", "if USE_SPIRAL:\n", " fname = \"simulations/coils/coil_12_spiral\"\n", "\n", "# write the coil out in a format that can be simulated\n", "p_f = rotate(points_f, 90)\n", "p_b = rotate(points_b, 90)\n", "\n", "with open(fname + \".csv\", \"w\") as f:\n", " for point in p_f[::-1]:\n", " f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n", "\n", "# two layer board\n", "with open(fname + \"-2-layer.csv\", \"wt\") as f:\n", " for point in p_f[::-1]:\n", " f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n", " for point in p_b:\n", " f.write(f\"{point[0]},{point[1]},0-0.062,0.5\\n\")\n", "\n", "# all four layer board\n", "with open(fname + \"-4-layer.csv\", \"wt\") as f:\n", " for point in p_f[::-1]:\n", " f.write(f\"{point[0]},{point[1]},0,0.5\\n\")\n", " for point in p_b:\n", " f.write(f\"{point[0]},{point[1]},0-0.011,0.5\\n\")\n", " for point in p_f[::-1]:\n", " f.write(f\"{point[0]},{point[1]},0-(0.011+0.04),0.5\\n\")\n", " for point in p_b:\n", " f.write(f\"{point[0]},{point[1]},0-(0.011+0.011+0.04),0.5\\n\")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# write the coil out in a format that can be simulated\n", "# shift the coils aronnd to make connections a bit easier\n", "pf = rotate(points_f, 90)\n", "pb = rotate(points_b, 90)\n", "fname = \"simulations/coils/coil_12_custom\"\n", "if USE_SPIRAL:\n", " fname = \"simulations/coils/coil_12_spiral\"\n", "\n", "with open(fname + \".csv\", \"w\") as f:\n", " for point in pf:\n", " f.write(f\"{point[0]/10},{point[1]/10},0,0.5\\n\")\n", "\n", "# two layer board\n", "with open(fname + \"-2-layer.csv\", \"wt\") as f:\n", " for point in pf[::-1]:\n", " f.write(f\"{point[0]/10},{point[1]/10},0,0.5\\n\")\n", " for point in pb:\n", " f.write(f\"{point[0]/10},{point[1]/10},0-0.062,0.5\\n\")\n", "\n", "# all four layer board\n", "with open(fname + \"-4-layer.csv\", \"wt\") as f:\n", " for point in pf[::-1]:\n", " f.write(f\"{point[0]/10},{point[1]/10},0,0.5\\n\")\n", " for point in pb:\n", " f.write(f\"{point[0]/10},{point[1]/10},0-0.011,0.5\\n\")\n", " for point in pf[::-1]:\n", " f.write(f\"{point[0]/10},{point[1]/10},0-(0.011+0.04),0.5\\n\")\n", " for point in pb:\n", " f.write(f\"{point[0]/10},{point[1]/10},0-(0.011+0.011+0.04),0.5\\n\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Generate PCB Layout" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# calculate the total length of the track to compute the resistance\n", "total_length_front = 0\n", "for i in range(len(points_f) - 1):\n", " total_length_front += np.linalg.norm(\n", " np.array(points_f[i + 1]) - np.array(points_f[i])\n", " )\n", "print(\"Total length front\", total_length_front)\n", "\n", "total_length_back = 0\n", "for i in range(len(points_b) - 1):\n", " total_length_back += np.linalg.norm(\n", " np.array(points_b[i + 1]) - np.array(points_b[i])\n", " )\n", "print(\"Total length back\", total_length_back)" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "vias = []\n", "tracks_f = []\n", "tracks_b = []\n", "pads = []\n", "pins = []\n", "mounting_holes = []\n", "silk = []\n", "components = []\n", "\n", "# shift the coils around to make connections a bit easier\n", "COIL_ROTATION = -360 / 12\n", "\n", "coil_angles = []\n", "for i in range(12):\n", " angle = i * 360 / 12 + COIL_ROTATION\n", " coil_angles.append(angle)\n", "\n", "# the main coils\n", "coil_labels = [\"A\", \"B\", \"C\"]\n", "coils_f = []\n", "coils_b = []\n", "for i in range(12):\n", " angle = coil_angles[i]\n", " if (i // 3) % 2 == 0:\n", " coil_A_f = translate(rotate(points_f, angle), COIL_CENTER_RADIUS, angle)\n", " coil_A_b = translate(rotate(points_b, angle), COIL_CENTER_RADIUS, angle)\n", " else:\n", " # slightly nudge the coils so that they don't overlap when flipped\n", " coil_A_f = translate(rotate(flip_y(points_f), angle), COIL_CENTER_RADIUS, angle)\n", " coil_A_b = translate(rotate(flip_y(points_b), angle), COIL_CENTER_RADIUS, angle)\n", " # keep track of the coils\n", " coils_f.append(coil_A_f)\n", " coils_b.append(coil_A_b)\n", "\n", " tracks_f.append({\"net\": COIL_NET_NAME, \"pts\": coil_A_f})\n", " tracks_b.append({\"net\": COIL_NET_NAME, \"pts\": coil_A_b})\n", " vias.append(create_via(get_arc_point(angle, COIL_VIA_RADIUS), COIL_NET_NAME))\n", " silk.append(\n", " create_silk(get_arc_point(angle, COIL_CENTER_RADIUS), coil_labels[i % 3])\n", " )\n", "\n", "# raidus for connecting the bottoms of the coils together\n", "connection_radius1 = STATOR_HOLE_RADIUS + 3 * TRACK_SPACING\n", "\n", "# create tracks to link the A coils around the center\n", "connection_via_radius_A = connection_radius1 + 3 * TRACK_SPACING + VIA_DIAM / 2\n", "coil_A1_A2_inner = (\n", " [get_arc_point(coil_angles[0], connection_via_radius_A)]\n", " + draw_arc(COIL_ROTATION, coil_angles[3], connection_radius1)\n", " + [get_arc_point(coil_angles[3], connection_via_radius_A)]\n", ")\n", "tracks_f.append({\"net\": COIL_NET_NAME, \"pts\": coil_A1_A2_inner})\n", "coil_A3_A4_inner = (\n", " [get_arc_point(coil_angles[6], connection_via_radius_A)]\n", " + draw_arc(coil_angles[6], coil_angles[9], connection_radius1)\n", " + [get_arc_point(coil_angles[9], connection_via_radius_A)]\n", ")\n", "tracks_f.append({\"net\": COIL_NET_NAME, \"pts\": coil_A3_A4_inner})\n", "# connect up the bottoms of the A coils\n", "coils_b[0].append(coil_A1_A2_inner[0])\n", "coils_b[3].append(coil_A1_A2_inner[-1])\n", "coils_b[6].append(coil_A3_A4_inner[0])\n", "coils_b[9].append(coil_A3_A4_inner[-1])\n", "# add the vias to stitch them together\n", "vias.append(create_via(coil_A1_A2_inner[0], COIL_NET_NAME))\n", "vias.append(create_via(coil_A1_A2_inner[-1], COIL_NET_NAME))\n", "vias.append(create_via(coil_A3_A4_inner[0], COIL_NET_NAME))\n", "vias.append(create_via(coil_A3_A4_inner[-1], COIL_NET_NAME))\n", "\n", "# create tracks to link the B coils around the center - this can all be done on the bottom layer\n", "coil_B1_B2_inner = draw_arc(coil_angles[1], coil_angles[4], connection_radius1)\n", "tracks_b.append({\"net\": COIL_NET_NAME, \"pts\": coil_B1_B2_inner})\n", "coil_B3_B4_inner = draw_arc(coil_angles[7], coil_angles[10], connection_radius1)\n", "tracks_b.append({\"net\": COIL_NET_NAME, \"pts\": coil_B3_B4_inner})\n", "# connect up the bottoms of the A coils\n", "coils_b[1].append(coil_B1_B2_inner[0])\n", "coils_b[4].append(coil_B1_B2_inner[-1])\n", "coils_b[7].append(coil_B3_B4_inner[0])\n", "coils_b[10].append(coil_B3_B4_inner[-1])\n", "\n", "# create tracks to link the C coils around the center\n", "connection_via_radius_C = connection_via_radius_A + 3 * TRACK_SPACING + VIA_DIAM / 2\n", "coil_C1_C2_inner = draw_arc(coil_angles[2], coil_angles[5], connection_via_radius_C)\n", "tracks_f.append({\"net\": COIL_NET_NAME, \"pts\": coil_C1_C2_inner})\n", "coil_C3_C4_inner = draw_arc(coil_angles[8], coil_angles[11], connection_via_radius_C)\n", "tracks_f.append({\"net\": COIL_NET_NAME, \"pts\": coil_C3_C4_inner})\n", "# connect up the bottoms of the B coils\n", "coils_b[2].append(coil_C1_C2_inner[0])\n", "coils_b[5].append(coil_C1_C2_inner[-1])\n", "coils_b[8].append(coil_C3_C4_inner[0])\n", "coils_b[11].append(coil_C3_C4_inner[-1])\n", "# add the vias to stitch them together\n", "vias.append(create_via(coil_C1_C2_inner[0], COIL_NET_NAME))\n", "vias.append(create_via(coil_C1_C2_inner[-1], COIL_NET_NAME))\n", "vias.append(create_via(coil_C3_C4_inner[0], COIL_NET_NAME))\n", "vias.append(create_via(coil_C3_C4_inner[-1], COIL_NET_NAME))\n", "\n", "# connect the last three coils together\n", "common_connection_radius = OUTER_CONN_RADIUS\n", "print(common_connection_radius)\n", "tracks_f.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": draw_arc(coil_angles[9], coil_angles[11], common_connection_radius),\n", " }\n", ")\n", "coils_f[9].append(get_arc_point(coil_angles[9], common_connection_radius))\n", "coils_f[10].append(get_arc_point(coil_angles[10], common_connection_radius))\n", "coils_f[11].append(get_arc_point(coil_angles[11], common_connection_radius))\n", "\n", "# connect the outer A coils together\n", "outer_connection_radius_A = OUTER_CONN_RADIUS\n", "tracks_f.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": draw_arc(coil_angles[3], coil_angles[6], outer_connection_radius_A),\n", " }\n", ")\n", "coils_f[3].append(get_arc_point(coil_angles[3], outer_connection_radius_A))\n", "coils_f[6].append(get_arc_point(coil_angles[6], outer_connection_radius_A))\n", "\n", "# connect the outer B coils together\n", "outer_connection_radius_B = outer_connection_radius_A - TRACK_SPACING - VIA_DIAM / 2\n", "tracks_b.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": [get_arc_point(coil_angles[4], outer_connection_radius_B)]\n", " + draw_arc(coil_angles[4], coil_angles[7], outer_connection_radius_A)\n", " + [get_arc_point(coil_angles[7], outer_connection_radius_B)],\n", " }\n", ")\n", "coils_f[4].append(get_arc_point(coil_angles[4], outer_connection_radius_B))\n", "coils_f[7].append(get_arc_point(coil_angles[7], outer_connection_radius_B))\n", "vias.append(\n", " create_via(\n", " get_arc_point(4 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B),\n", " COIL_NET_NAME,\n", " )\n", ")\n", "vias.append(\n", " create_via(\n", " get_arc_point(7 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B),\n", " COIL_NET_NAME,\n", " )\n", ")\n", "\n", "# connect the outer C coilds together\n", "outer_connection_radius_C = outer_connection_radius_B - TRACK_SPACING - VIA_DIAM / 2\n", "tracks_b.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": draw_arc(\n", " 5 * 360 / 12 + COIL_ROTATION,\n", " 8 * 360 / 12 + COIL_ROTATION,\n", " outer_connection_radius_C,\n", " ),\n", " }\n", ")\n", "coils_f[5].append(\n", " get_arc_point(5 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C)\n", ")\n", "coils_f[8].append(\n", " get_arc_point(8 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C)\n", ")\n", "vias.append(\n", " create_via(\n", " get_arc_point(5 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C),\n", " COIL_NET_NAME,\n", " )\n", ")\n", "vias.append(\n", " create_via(\n", " get_arc_point(8 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C),\n", " COIL_NET_NAME,\n", " )\n", ")\n", "\n", "# create the pads for connecting the inputs to the coils\n", "if PAD_ENABLE:\n", " silk.append(\n", " create_silk(\n", " (INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, PAD_PITCH), \"C\", \"b\", 2.5, -900\n", " )\n", " )\n", " silk.append(\n", " create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, 0), \"B\", \"b\", 2.5, -900)\n", " )\n", " silk.append(\n", " create_silk(\n", " (INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, -PAD_PITCH), \"A\", \"b\", 2.5, -900\n", " )\n", " )\n", "\n", " pads.append(\n", " create_pad((INPUT_PAD_RADIUS, -PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"),\n", " COIL_NET_NAME,\n", " )\n", " pads.append(\n", " create_pad((INPUT_PAD_RADIUS, 0), PAD_WIDTH, PAD_HEIGHT, \"b\"), COIL_NET_NAME\n", " )\n", " pads.append(\n", " create_pad((INPUT_PAD_RADIUS, PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"),\n", " COIL_NET_NAME,\n", " )\n", "\n", " # connect coil A to the top pad\n", " pad_connection_point_x = INPUT_PAD_RADIUS\n", " pad_angle = np.rad2deg(np.arcsin(PAD_PITCH / pad_connection_point_x))\n", " coils_f[0].append(get_arc_point(coil_angles[0], pad_connection_point_x))\n", " vias.append(\n", " create_via(get_arc_point(coil_angles[0], pad_connection_point_x), COIL_NET_NAME)\n", " )\n", " # connect coil B to the middle pad\n", " coils_f[1].append((pad_connection_point_x + PAD_WIDTH / 2 + VIA_DIAM / 2, 0))\n", " vias.append(\n", " create_via(\n", " ((pad_connection_point_x + PAD_WIDTH / 2 + VIA_DIAM / 2, 0)), COIL_NET_NAME\n", " )\n", " )\n", " # connect coil C to the bottom pad\n", " coils_f[2].append(get_arc_point(coil_angles[2], pad_connection_point_x))\n", " vias.append(\n", " create_via(get_arc_point(coil_angles[2], pad_connection_point_x), COIL_NET_NAME)\n", " )\n", "\n", "elif COIL_INPUT_VIAS_ENABLE:\n", "\n", " # create vias for connecting the inputs to the coils\n", " for i in range(3):\n", " coils_f[i].append(get_arc_point(coil_angles[i], COIL_INPUT_VIA_RADIUS))\n", " vias.append(\n", " create_via(\n", " get_arc_point(coil_angles[i], COIL_INPUT_VIA_RADIUS), COIL_NET_NAME\n", " )\n", " )" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Multi-Layer" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# if we are doing multiple layers then duplicate the front and back layers\n", "tracks_in = []\n", "if LAYERS >= 4:\n", " tracks_in.append(tracks_b.copy())\n", " tracks_in.append(tracks_f.copy())\n", "if LAYERS >= 6:\n", " tracks_in.append(tracks_b.copy())\n", " tracks_in.append(tracks_f.copy())\n", "if LAYERS == 8:\n", " tracks_in.append(tracks_b.copy())\n", " tracks_in.append(tracks_f.copy())" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Led Ring" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# LED ring wiring\n", "if LED_RING_ENABLE:\n", "\n", " # LED wiring\n", " for i in range(LED_RING_NUM_LEDS):\n", "\n", " # Angle calculation\n", " angle = i * 360 / LED_RING_NUM_LEDS + COIL_ROTATION\n", " next_angle = (i + 1) * 360 / LED_RING_NUM_LEDS + COIL_ROTATION\n", "\n", " # Vias for power\n", " vias.append(\n", " create_via(\n", " get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS), LED_GND_NET_NAME\n", " )\n", " )\n", " vias.append(\n", " create_via(\n", " get_arc_point(angle, LED_RING_INNER_VIA_RADIUS), LED_VPLUS_NET_NAME\n", " )\n", " )\n", "\n", " # Interconnect tracks for power\n", " tracks_f.append(\n", " {\n", " \"net\": LED_GND_NET_NAME,\n", " \"width\": LED_POWER_INTERCONN_TRACK_WIDTH,\n", " \"pts\": [\n", " get_arc_point(\n", " angle + LED_CHIP_PAD_ANGLE_FROM_CENTRE,\n", " LED_RING_RADIUS + LED_CHIP_PAD_RADIAL_FROM_CENTRE,\n", " ),\n", " get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS),\n", " ],\n", " }\n", " )\n", " tracks_f.append(\n", " {\n", " \"net\": LED_VPLUS_NET_NAME,\n", " \"width\": LED_POWER_INTERCONN_TRACK_WIDTH,\n", " \"pts\": [\n", " get_arc_point(\n", " angle - LED_CHIP_PAD_ANGLE_FROM_CENTRE,\n", " LED_RING_RADIUS - LED_CHIP_PAD_RADIAL_FROM_CENTRE,\n", " ),\n", " get_arc_point(angle, LED_RING_INNER_VIA_RADIUS),\n", " ],\n", " }\n", " )\n", "\n", " # Rails for power\n", " if i + 1 != LED_INTERCONN_TO_SUPPRESS:\n", " tracks_b.append(\n", " {\n", " \"net\": LED_GND_NET_NAME,\n", " \"width\": LED_POWER_TRACK_WIDTH,\n", " \"pts\": [\n", " get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS),\n", " get_arc_point(next_angle, LED_RING_OUTER_VIA_RADIUS),\n", " ],\n", " }\n", " )\n", " tracks_b.append(\n", " {\n", " \"net\": LED_VPLUS_NET_NAME,\n", " \"width\": LED_POWER_TRACK_WIDTH,\n", " \"pts\": [\n", " get_arc_point(angle, LED_RING_INNER_VIA_RADIUS),\n", " get_arc_point(next_angle, LED_RING_INNER_VIA_RADIUS),\n", " ],\n", " }\n", " )\n", "\n", " # LED chip interconnect\n", " if i + 1 != LED_INTERCONN_TO_SUPPRESS:\n", " tracks_f.append(\n", " {\n", " \"net\": LED_IO_NET_NAME_BASE + str(i + 1),\n", " \"pts\": [\n", " get_arc_point(\n", " angle - LED_CHIP_PAD_ANGLE_FROM_CENTRE,\n", " LED_RING_RADIUS + LED_CHIP_PAD_RADIAL_FROM_CENTRE,\n", " ),\n", " get_arc_point(\n", " next_angle - LED_PAD_ANGLE_OFFSET,\n", " LED_RING_RADIUS - LED_CHIP_PAD_RADIAL_FROM_CENTRE,\n", " ),\n", " ],\n", " }\n", " )\n", "\n", " # LED chip components\n", " components.append(\n", " {\n", " \"ref\": LED_REF_BASE + str(i + 1),\n", " \"pads\": [\n", " {\n", " \"num\": 1,\n", " \"net\": LED_IO_NET_NC\n", " if i + 1 == LED_RING_END_LED_REF\n", " else LED_IO_NET_NAME_BASE + str(i + 1),\n", " },\n", " {\n", " \"num\": 2,\n", " \"net\": LED_GND_NET_NAME,\n", " },\n", " {\n", " \"num\": 3,\n", " \"net\": LED_IO_NET_INPUT\n", " if i + 1 == LED_RING_START_LED_REF\n", " else LED_IO_NET_NAME_BASE\n", " + str((i + 1) % LED_RING_NUM_LEDS + 1),\n", " },\n", " {\n", " \"num\": 4,\n", " \"net\": LED_VPLUS_NET_NAME,\n", " },\n", " ],\n", " }\n", " )" ] }, { "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "# Generate JSON" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Generate the JSON output file\n", "if PAD_ENABLE:\n", " # these final bits of wiring up to the input pads don't need to be duplicated\n", " tracks_b.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": [\n", " (pad_connection_point_x + PAD_WIDTH / 2, 0),\n", " (pad_connection_point_x, 0),\n", " ],\n", " }\n", " )\n", " tracks_b.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": draw_arc(coil_angles[0], -pad_angle, pad_connection_point_x, 1),\n", " }\n", " )\n", " tracks_b.append(\n", " {\n", " \"net\": COIL_NET_NAME,\n", " \"pts\": draw_arc(coil_angles[2], pad_angle, pad_connection_point_x, 1),\n", " }\n", " )\n", "\n", "nibble_angle_size = 360 * SCREW_HOLE_DRILL_DIAM / (2 * np.pi * STATOR_RADIUS)\n", "\n", "if PCB_EDGE_CUTS:\n", " outer_cuts = (\n", " draw_arc(\n", " -45 + nibble_angle_size / 2, 45 - nibble_angle_size / 2, STATOR_RADIUS, 5\n", " )\n", " + translate(\n", " rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5)[::-1], 135),\n", " STATOR_RADIUS,\n", " 45,\n", " )\n", " + draw_arc(\n", " 45 + nibble_angle_size / 2, 135 - nibble_angle_size / 2, STATOR_RADIUS, 5\n", " )\n", " + translate(\n", " rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 225)[::-1],\n", " STATOR_RADIUS,\n", " 135,\n", " )\n", " + draw_arc(\n", " 135 + nibble_angle_size / 2, 225 - nibble_angle_size / 2, STATOR_RADIUS, 5\n", " )\n", " + translate(\n", " rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 315)[::-1],\n", " STATOR_RADIUS,\n", " 225,\n", " )\n", " + draw_arc(\n", " 225 + nibble_angle_size / 2, 315 - nibble_angle_size / 2, STATOR_RADIUS, 5\n", " )\n", " + translate(\n", " rotate(draw_arc(5, 175, SCREW_HOLE_DRILL_DIAM / 2, 5), 45)[::-1],\n", " STATOR_RADIUS,\n", " 315,\n", " )\n", " )\n", "\n", " edge_cuts = [\n", " outer_cuts,\n", " draw_arc(0, 360, STATOR_HOLE_RADIUS, 1),\n", " ]\n", "else:\n", " edge_cuts = []\n", "\n", "# dump out the json version\n", "json_result = dump_json(\n", " filename=f\"coils_12_{STATOR_RADIUS}mm.json\",\n", " track_width=TRACK_WIDTH,\n", " pin_diam=PIN_DIAM,\n", " pin_drill=PIN_DRILL,\n", " via_diam=VIA_DIAM,\n", " via_drill=VIA_DRILL,\n", " vias=vias,\n", " pins=pins,\n", " pads=pads,\n", " silk=silk,\n", " tracks_f=tracks_f,\n", " tracks_in=tracks_in,\n", " tracks_b=tracks_b,\n", " mounting_holes=mounting_holes,\n", " edge_cuts=edge_cuts,\n", " components=components,\n", ")" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# plot the json\n", "plot_json(json_result)" ] } ], "metadata": { "kernelspec": { "display_name": ".venv", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.11.0" }, "vscode": { "interpreter": { "hash": "97df458a008df4bb35f481235dd1a04940c0d868e77540a7386d9922f34deb97" } } }, "nbformat": 4, "nbformat_minor": 2 }