pcb-stator-coil-generator/coil_generator-12.ipynb

1002 lines
1.2 MiB
Text
Raw Normal View History

2022-10-10 18:45:58 +00:00
{
"cells": [
{
"cell_type": "code",
"execution_count": 141,
2022-10-10 18:45:58 +00:00
"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",
2022-10-12 09:33:06 +00:00
" optimize_points,\n",
" chaikin,\n",
" rotate_point,\n",
2022-11-02 18:24:06 +00:00
" scale,\n",
2022-10-12 09:33:06 +00:00
")\n",
"from pcb_json import (\n",
" dump_json,\n",
" plot_json,\n",
" create_via,\n",
" create_pad,\n",
2022-10-12 09:33:06 +00:00
" create_pin,\n",
" create_silk,\n",
" create_silk,\n",
2022-10-12 09:33:06 +00:00
" create_mounting_hole,\n",
")\n",
"\n",
2022-10-10 18:45:58 +00:00
"from enum import Enum\n",
"\n",
"Layer = Enum(\"Layer\", \"FRONT BACK\")"
]
},
{
"cell_type": "code",
"execution_count": 142,
2022-10-10 18:45:58 +00:00
"metadata": {},
"outputs": [],
"source": [
"# Track width and spacing\n",
"TRACK_WIDTH = 0.127\n",
"TRACK_SPACING = 0.127\n",
"\n",
"# via defaults\n",
2022-10-10 18:45:58 +00:00
"VIA_DIAM = 0.8\n",
"VIA_DRILL = 0.4\n",
"\n",
"# this is for a 1.27mm pitch pin\n",
2022-10-12 09:33:06 +00:00
"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",
2022-10-12 09:33:06 +00:00
"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\"\n"
2022-11-02 18:24:06 +00:00
]
},
{
"cell_type": "code",
"execution_count": 143,
2022-11-02 18:24:06 +00:00
"metadata": {},
"outputs": [],
"source": [
"# Standard 25 mm version\n",
"\n",
"# PCB Edge size\n",
"STATOR_RADIUS = 25\n",
2023-02-03 13:23:56 +00:00
"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"
2022-11-02 18:24:06 +00:00
]
},
{
"cell_type": "code",
"execution_count": 144,
2022-11-02 18:24:06 +00:00
"metadata": {},
"outputs": [],
"source": [
"# Large 35 mm version\n",
2022-10-10 18:45:58 +00:00
"\n",
2022-11-02 18:24:06 +00:00
"# PCB Edge size\n",
"STATOR_RADIUS = 35\n",
2022-11-02 18:24:06 +00:00
"\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",
2022-11-19 08:24:27 +00:00
"# for custom shape\n",
2022-11-02 18:24:06 +00:00
"TURNS = 16\n",
2022-11-19 08:24:27 +00:00
"# for spiral\n",
"# TURNS = 17\n",
2022-11-02 18:24:06 +00:00
"COIL_CENTER_RADIUS = 19.95\n",
"COIL_VIA_RADIUS = 20.95\n",
"\n",
"# Coil input vias\n",
"COIL_INPUT_VIA_RADIUS = 27"
2022-11-02 18:24:06 +00:00
]
},
{
"cell_type": "code",
"execution_count": 145,
2022-11-02 18:24:06 +00:00
"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 = .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": 146,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"20.95\n"
]
}
],
2022-11-02 18:24:06 +00:00
"source": [
"# where to put the input pads\n",
"INPUT_PAD_RADIUS = COIL_VIA_RADIUS\n",
"print(INPUT_PAD_RADIUS)\n",
2022-10-10 18:45:58 +00:00
"\n",
2023-02-03 13:23:56 +00:00
"USE_SPIRAL = False\n",
2022-11-19 08:24:27 +00:00
"\n",
"if USE_SPIRAL:\n",
" TURNS = 18\n",
" COIL_VIA_RADIUS = 20.5\n",
" COIL_CENTER_RADIUS = 20.5\n",
2022-10-28 13:11:33 +00:00
"\n",
"LAYERS = 8\n",
"\n",
"OUTER_CONN_RADIUS = 28"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Arbitrary Coil Generation"
]
},
{
"cell_type": "code",
"execution_count": 147,
2022-10-10 18:45:58 +00:00
"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",
2022-10-10 18:45:58 +00:00
" (1.9, 0.0),\n",
" (1.9, 1.45),\n",
" (-3.5, 0.01),\n",
2022-10-10 18:45:58 +00:00
"]"
]
},
{
"cell_type": "code",
"execution_count": 148,
2022-10-10 18:45:58 +00:00
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(-3.77, 2.17, -1.595, 1.595)"
]
},
"execution_count": 148,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAi8AAAGwCAYAAABhDIVPAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/P9b71AAAACXBIWXMAAA9hAAAPYQGoP6dpAAArnUlEQVR4nO3df3RU9Z3/8dcMkoEICQbyEyYaoAUrCBh+mOhCKNGAv5ot5VjtkR8iVA+4xbAq8bRw1LXRVUssUgPbLRgrR7Qu2ILHU4z8LEEFzaocyW4QNpgwIYrMQJQEkvn+kS+D0RAyydy59848H+fcU+bm3rlv5qh59TOvuePw+/1+AQAA2ITT7AEAAACCQXgBAAC2QngBAAC2QngBAAC2QngBAAC2QngBAAC2QngBAAC2conZA4RaS0uLamtr1bdvXzkcDrPHAQAAneD3+3Xy5EmlpaXJ6ex4bSXiwkttba3cbrfZYwAAgC44cuSIBg0a1OExERde+vbtK6n1Lx8XF2fyNAAAoDN8Pp/cbnfg93hHIi68nHurKC4ujvACAIDNdKbyQWEXAADYCuEFAADYCuEFAADYSsR1XgAAsLPm5madOXPG7DEMERMTc9GPQXcG4QUAAAvw+/3yeDw6ceKE2aMYxul0KiMjQzExMd16HsILAAAWcC64JCUlKTY2NuJutHruJrJHjx5Venp6t/5+hBcAAEzW3NwcCC79+/c3exzDJCYmqra2VmfPnlXPnj27/DwUdgEAMNm5jktsbKzJkxjr3NtFzc3N3XoewgsAABYRaW8VfVeo/n6EFwAAYCuEFwAAYCuGhpeioiKNGzdOffv2VVJSkvLz81VZWXnR81577TUNHz5cvXr10siRI/Xmm28aOSYAALARQ8PL9u3btWDBAu3Zs0dbtmzRmTNndOONN6qhoeGC5+zevVt33HGH5s6dqw8//FD5+fnKz8/XJ598YuSoAADAJhx+v98frovV19crKSlJ27dv18SJE9s95vbbb1dDQ4M2bdoU2Hfttddq9OjRKikpueg1fD6f4uPj5fV6+VZpAIAtnD59WocOHVJGRoZ69epl9jiG6ejvGczv77De58Xr9UqSEhISLnhMeXm5CgoK2uzLy8vTxo0b2z2+sbFRjY2Ngcc+n6/7gwIAYDK/X/r66/BfNzZW6uyHgkpLS/XAAw+otrZWLpcrsD8/P199+/bVSy+9ZMiMYSvstrS0aNGiRbruuus0YsSICx7n8XiUnJzcZl9ycrI8Hk+7xxcVFSk+Pj6wud3ukM4NAIAZvv5a6tMn/FswgWnGjBlqbm7WX//618C+Y8eOafPmzbr77rsNeFVahS28LFiwQJ988oleeeWVkD5vYWGhvF5vYDty5EhInx8AALSvd+/euvPOO7VmzZrAvj//+c9KT09XTk6OYdcNy9tGCxcu1KZNm7Rjxw4NGjSow2NTUlJUV1fXZl9dXZ1SUlLaPd7lcrVZqgIAIBLExkqnTplz3WDMmzdP48aNU01NjQYOHKi1a9dq9uzZht5wz9Dw4vf7df/992vDhg3atm2bMjIyLnpOVlaWysrKtGjRosC+LVu2KCsry8BJAQCwFodDuvRSs6e4uDFjxmjUqFEqLS3VjTfeqP3792vz5s2GXtPQ8LJgwQKtW7dOb7zxhvr27RvorcTHx6t3796SpJkzZ2rgwIEqKiqSJP3qV7/SpEmT9Oyzz+rmm2/WK6+8or1792r16tVGjgoAALronnvuUXFxsWpqapSbm2t4/9TQzssLL7wgr9ernJwcpaamBrb169cHjqmurtbRo0cDj7Ozs7Vu3TqtXr1ao0aN0l/+8hdt3Lixw5IvAAAwz5133qnPP/9c//Ef/2FoUfecsN7nJRy4zwsAwG4i4T4vM2fO1ObNm7/3selvC9V9XvhuIwAA0G01NTX6xS9+EZYP0YT1JnUAACCyfPXVV9q2bZu2bdumP/zhD2G5JuEFAAB02ZgxY/TVV1/pqaee0rBhw8JyTcILAADossOHD4f9mnReAACwiAj7DM33hOrvR3gBAMBkPXv2lCR9bcY3MYZRU1OTJKlHjx7deh7eNgIAwGQ9evRQv379dOzYMUlSbGysobfXN0NLS4vq6+sVGxurSy7pXvwgvAAAYAHnvsPvXICJRE6nU+np6d0OZoQXAAAswOFwKDU1VUlJSTpz5ozZ4xgiJiZGTmf3GyuEFwAALKRHjx7d7oREOgq7AADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVggvAADAVgwNLzt27NCtt96qtLQ0ORwObdy4scPjt23bJofD8b3N4/EYOSYAALARQ8NLQ0ODRo0apZUrVwZ1XmVlpY4ePRrYkpKSDJoQAADYzSVGPvm0adM0bdq0oM9LSkpSv379Qj8QAACwPUt2XkaPHq3U1FTdcMMN+sc//tHhsY2NjfL5fG02AAAQuSwVXlJTU1VSUqLXX39dr7/+utxut3JycvTBBx9c8JyioiLFx8cHNrfbHcaJAQBAuDn8fr8/LBdyOLRhwwbl5+cHdd6kSZOUnp6ul156qd2fNzY2qrGxMfDY5/PJ7XbL6/UqLi6uOyMDAIAw8fl8io+P79Tvb0M7L6Ewfvx47dq164I/d7lccrlcYZwIAIDo5PdLtbVScrJ0iYkJwlJvG7WnoqJCqampZo8BAEBUqquT1q2T5syR0tOlQYOkO+4wdyZDc9OpU6dUVVUVeHzo0CFVVFQoISFB6enpKiwsVE1NjUpLSyVJxcXFysjI0FVXXaXTp0/rj3/8o9555x39/e9/N3JMAADw/506Je3cKW3ZIr39tvTxx98/pr194WRoeNm7d68mT54ceFxQUCBJmjVrltauXaujR4+quro68POmpiYtXrxYNTU1io2N1dVXX6233367zXMAAIDQOXtW2rv3fFgpL5fOnDn/c4dDGjNGys2VLrtMKiw0b9bATOEq7IZLMIUfAACijd8v/c//nA8rW7dK373LyBVXSDfc0BpYfvxjacCA1v07d0oTJ0rDhkkHDoR2rogq7AIAgO6pq5PKys4Hls8/b/vzyy6TpkxpDSs33CANHmzOnJ1FeAEAIMJcrLficknXX38+rIweLfXoYcqoXUJ4AQDA5oLprdxwg3TddVLv3ubN212EFwAAbKY7vZVIQHgBAMAGIq230h2EFwAALCjSeyvdQXgBAMACoq230h2EFwAATBDtvZXuILwAABAm9FZCg/ACAIBB6K0Yg/ACAECI0FsJD8ILAABdRG/FHIQXAACCQG/FfIQXAAA6QG/FeggvAAB8C70V6yO8AACiGr0V+yG8AACiDr0VeyO8AAAiHr2VyEJ4AQBEHHorkY3wAgCwPXor0YXwAgCwJXor0YvwAgCwBXorOIfwAgCwJHoruBDCCwDAEuitoLMILwAA09BbQVcQXgAAYUNvBaFAeAEAGIbeCoxAeAEAhAy9FYQD4QUA0C30VhBuhBcAQFDorcBshBcAQIforcBqnEY++Y4dO3TrrbcqLS1NDodDGzduvOg527Zt0zXXXCOXy6WhQ4dq7dq1Ro4IAPgOv1+qrJSef17Kz5f695eysqSlS6UdO1qDyxVXSPPmSevXS8eOSfv2SU891RpgCC4wmqErLw0NDRo1apTuvvtu/fSnP73o8YcOHdLNN9+se++9Vy+//LLKysp0zz33KDU1VXl5eUaOCgBRjd4K7MTQ8DJt2jRNmzat08eXlJQoIyNDzz77rCTpyiuv1K5du7R8+fILhpfGxkY1NjYGHvu+W2sHAHwPvRXYmaU6L+Xl5crNzW2zLy8vT4sWLbrgOUVFRXr00UcNngwA7I3eCiKJpcKLx+NRcnJym33Jycny+Xz65ptv1Ludf5MKCwtVUFAQeOzz+eR2uw2fFQCsjPutIJJZKrx0hcvlksvlMnsMADAdvRVEC0uFl5SUFNXV1bXZV1dXp7i
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
2022-10-10 18:45:58 +00:00
"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": 149,
2022-10-10 18:45:58 +00:00
"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"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "code",
"execution_count": 150,
2022-10-10 18:45:58 +00:00
"metadata": {},
"outputs": [],
"source": [
2022-11-19 08:24:27 +00:00
"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": 151,
2022-11-19 08:24:27 +00:00
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Not using spiral\n",
"Optimised from 128 to 66 points\n",
"Optimised from 128 to 66 points\n"
]
}
],
2022-11-19 08:24:27 +00:00
"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",
2022-10-10 18:45:58 +00:00
"\n",
2022-11-19 08:24:27 +00:00
" 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": 152,
2022-11-19 08:24:27 +00:00
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Track points 262 262\n"
]
},
{
"data": {
"image/png": "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
"text/plain": [
"<Figure size 640x480 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
2022-11-19 08:24:27 +00:00
"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",
2022-10-10 18:45:58 +00:00
"\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",
2022-10-10 18:45:58 +00:00
"\n",
"print(\"Track points\", len(points_f), len(points_b))"
2022-10-10 18:45:58 +00:00
]
},
2022-11-02 18:24:06 +00:00
{
"cell_type": "code",
"execution_count": 153,
2022-11-02 18:24:06 +00:00
"metadata": {},
"outputs": [],
"source": [
"# write the coil out in a format that can be simulated\n",
2022-11-19 08:24:27 +00:00
"# 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",
2022-11-02 18:24:06 +00:00
"\n",
2022-11-19 08:24:27 +00:00
"# 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": 154,
2022-11-19 08:24:27 +00:00
"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\")"
2022-11-02 18:24:06 +00:00
]
},
2022-10-10 18:45:58 +00:00
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate PCB Layout"
]
},
{
"cell_type": "code",
"execution_count": 155,
2022-10-10 18:45:58 +00:00
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Total length front 480.66738294773955\n",
"Total length back 484.23084697358496\n"
]
}
],
2022-10-10 18:45:58 +00:00
"source": [
"# calculate the total length of the track to compute the resistance\n",
2022-10-10 18:45:58 +00:00
"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": 156,
2022-10-10 18:45:58 +00:00
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"28\n"
]
}
],
2022-10-10 18:45:58 +00:00
"source": [
"vias = []\n",
"tracks_f = []\n",
"tracks_b = []\n",
"pads = []\n",
2022-10-12 09:33:06 +00:00
"pins = []\n",
"mounting_holes = []\n",
2022-10-10 18:45:58 +00:00
"silk = []\n",
"components = []\n",
2022-10-10 18:45:58 +00:00
"\n",
"# shift the coils around to make connections a bit easier\n",
"COIL_ROTATION = -360 / 12\n",
2022-10-10 18:45:58 +00:00
"\n",
"coil_angles = []\n",
"for i in range(12):\n",
" angle = i * 360 / 12 + COIL_ROTATION\n",
" coil_angles.append(angle)\n",
2022-10-10 18:45:58 +00:00
"\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",
2022-10-10 18:45:58 +00:00
" 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",
2022-10-12 16:10:13 +00:00
" # 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",
2022-10-10 18:45:58 +00:00
" # 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",
2022-10-10 18:45:58 +00:00
" 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",
2022-10-12 19:18:28 +00:00
"connection_radius1 = STATOR_HOLE_RADIUS + 3 * TRACK_SPACING\n",
2022-10-10 18:45:58 +00:00
"\n",
"# create tracks to link the A coils around the center\n",
2022-10-12 19:18:28 +00:00
"connection_via_radius_A = connection_radius1 + 3 * TRACK_SPACING + VIA_DIAM / 2\n",
2022-10-10 18:45:58 +00:00
"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",
2022-10-10 18:45:58 +00:00
")\n",
"tracks_f.append({\"net\":COIL_NET_NAME,\"pts\":coil_A1_A2_inner})\n",
2022-10-10 18:45:58 +00:00
"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",
2022-10-10 18:45:58 +00:00
")\n",
"tracks_f.append({\"net\":COIL_NET_NAME,\"pts\":coil_A3_A4_inner})\n",
2022-10-10 18:45:58 +00:00
"# 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",
2022-10-10 18:45:58 +00:00
"\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",
2022-10-10 18:45:58 +00:00
"# 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",
2022-10-12 19:18:28 +00:00
"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",
2022-10-10 18:45:58 +00:00
"# 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",
2022-10-10 18:45:58 +00:00
"\n",
"# connect the last three coils together\n",
"common_connection_radius = OUTER_CONN_RADIUS\n",
"print(common_connection_radius)\n",
"tracks_f.append({\"net\":COIL_NET_NAME,\"pts\":draw_arc(coil_angles[9], coil_angles[11], common_connection_radius)})\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",
2022-10-10 18:45:58 +00:00
"\n",
"# connect the outer A coils together\n",
"outer_connection_radius_A = OUTER_CONN_RADIUS\n",
"tracks_f.append({\"net\":COIL_NET_NAME,\"pts\":draw_arc(coil_angles[3], coil_angles[6], outer_connection_radius_A)})\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",
2022-10-10 18:45:58 +00:00
"\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({\"net\":COIL_NET_NAME,\"pts\":\n",
" [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(get_arc_point(4 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B), COIL_NET_NAME)\n",
")\n",
"vias.append(\n",
" create_via(get_arc_point(7 * 360 / 12 + COIL_ROTATION, outer_connection_radius_B), COIL_NET_NAME)\n",
2022-10-10 18:45:58 +00:00
")\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({\"net\":COIL_NET_NAME,\"pts\":\n",
" 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(get_arc_point(5 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C), COIL_NET_NAME)\n",
")\n",
"vias.append(\n",
" create_via(get_arc_point(8 * 360 / 12 + COIL_ROTATION, outer_connection_radius_C), COIL_NET_NAME)\n",
")\n",
2022-10-12 09:33:06 +00:00
"\n",
"# create the pads for connecting the inputs to the coils\n",
"if PAD_ENABLE:\n",
" silk.append(\n",
" create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, PAD_PITCH), \"C\", \"b\", 2.5, -900)\n",
" )\n",
" silk.append(create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, 0), \"B\", \"b\", 2.5, -900))\n",
" silk.append(\n",
" create_silk((INPUT_PAD_RADIUS - PAD_HEIGHT - 2.5, -PAD_PITCH), \"A\", \"b\", 2.5, -900)\n",
" )\n",
2022-10-12 16:10:13 +00:00
"\n",
" pads.append(create_pad((INPUT_PAD_RADIUS, -PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"), COIL_NET_NAME)\n",
" pads.append(create_pad((INPUT_PAD_RADIUS, 0), PAD_WIDTH, PAD_HEIGHT, \"b\"), COIL_NET_NAME)\n",
" pads.append(create_pad((INPUT_PAD_RADIUS, PAD_PITCH), PAD_WIDTH, PAD_HEIGHT, \"b\"), COIL_NET_NAME)\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(create_via(get_arc_point(coil_angles[0], pad_connection_point_x), COIL_NET_NAME))\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(create_via(((pad_connection_point_x + PAD_WIDTH / 2 + VIA_DIAM / 2, 0)), COIL_NET_NAME))\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(create_via(get_arc_point(coil_angles[2], pad_connection_point_x), COIL_NET_NAME))\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(create_via(get_arc_point(coil_angles[i], COIL_INPUT_VIA_RADIUS), COIL_NET_NAME))\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Multi-Layer"
2022-10-10 18:45:58 +00:00
]
},
{
"cell_type": "code",
"execution_count": 157,
2022-10-10 18:45:58 +00:00
"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": 158,
"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(create_via(get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS), LED_GND_NET_NAME))\n",
" vias.append(create_via(get_arc_point(angle, LED_RING_INNER_VIA_RADIUS), LED_VPLUS_NET_NAME))\n",
"\n",
" # Interconnect tracks for power\n",
" tracks_f.append({\"net\":LED_GND_NET_NAME,\"width\": LED_POWER_INTERCONN_TRACK_WIDTH,\n",
" \"pts\":\n",
" [\n",
" get_arc_point(angle + LED_CHIP_PAD_ANGLE_FROM_CENTRE, LED_RING_RADIUS + LED_CHIP_PAD_RADIAL_FROM_CENTRE),\n",
" get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS)\n",
" ],\n",
" \n",
" })\n",
" tracks_f.append({\"net\":LED_VPLUS_NET_NAME,\"width\": LED_POWER_INTERCONN_TRACK_WIDTH,\n",
" \"pts\":\n",
" [\n",
" get_arc_point(angle - LED_CHIP_PAD_ANGLE_FROM_CENTRE, LED_RING_RADIUS - LED_CHIP_PAD_RADIAL_FROM_CENTRE),\n",
" get_arc_point(angle, LED_RING_INNER_VIA_RADIUS)\n",
" ],\n",
" })\n",
"\n",
" # Rails for power\n",
2023-02-04 11:17:28 +00:00
" if i + 1 != LED_INTERCONN_TO_SUPPRESS:\n",
" tracks_b.append({\"net\":LED_GND_NET_NAME,\"width\": LED_POWER_TRACK_WIDTH,\n",
" \"pts\":\n",
" [\n",
" get_arc_point(angle, LED_RING_OUTER_VIA_RADIUS),\n",
" get_arc_point(next_angle, LED_RING_OUTER_VIA_RADIUS)\n",
" ],\n",
" })\n",
" tracks_b.append({\"net\":LED_VPLUS_NET_NAME,\"width\": LED_POWER_TRACK_WIDTH,\n",
" \"pts\":\n",
" [\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",
" # LED chip interconnect\n",
" if i + 1 != LED_INTERCONN_TO_SUPPRESS:\n",
" tracks_f.append({\"net\":LED_IO_NET_NAME_BASE+str(i+1),\"pts\":\n",
" [\n",
" get_arc_point(angle - LED_CHIP_PAD_ANGLE_FROM_CENTRE, LED_RING_RADIUS + LED_CHIP_PAD_RADIAL_FROM_CENTRE),\n",
" get_arc_point(next_angle - LED_PAD_ANGLE_OFFSET, LED_RING_RADIUS - LED_CHIP_PAD_RADIAL_FROM_CENTRE)\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 if i+1 == LED_RING_END_LED_REF 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 if i+1 == LED_RING_START_LED_REF else LED_IO_NET_NAME_BASE+str((i+1) % LED_RING_NUM_LEDS + 1),\n",
" },\n",
" {\n",
" \"num\": 4,\n",
" \"net\":LED_VPLUS_NET_NAME,\n",
" },\n",
" ]\n",
" }\n",
" )\n",
" \n",
"\n"
]
},
{
"attachments": {},
"cell_type": "markdown",
"metadata": {},
"source": [
"# Generate JSON"
]
},
{
"cell_type": "code",
"execution_count": 159,
"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({\"net\":COIL_NET_NAME,\"pts\":\n",
" [(pad_connection_point_x + PAD_WIDTH / 2, 0), (pad_connection_point_x, 0)]\n",
" }\n",
" )\n",
" tracks_b.append({\"net\":COIL_NET_NAME,\"pts\":draw_arc(coil_angles[0], -pad_angle, pad_connection_point_x, 1)})\n",
" tracks_b.append({\"net\":COIL_NET_NAME,\"pts\":draw_arc(coil_angles[2], pad_angle, pad_connection_point_x, 1)})\n",
"\n",
2022-11-01 20:00:12 +00:00
"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(-45 + nibble_angle_size / 2, 45 - nibble_angle_size / 2, STATOR_RADIUS, 5)\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",
2022-11-19 08:24:27 +00:00
" )\n",
2022-11-01 20:00:12 +00:00
"\n",
" edge_cuts = [\n",
" outer_cuts,\n",
" draw_arc(0, 360, STATOR_HOLE_RADIUS, 1),\n",
" ]\n",
"else:\n",
" edge_cuts = [\n",
" ]\n",
2022-10-28 13:11:33 +00:00
"\n",
"# dump out the json version\n",
"json_result = dump_json(\n",
2022-11-02 18:24:06 +00:00
" filename=f\"coils_12_{STATOR_RADIUS}mm.json\",\n",
2022-10-28 13:11:33 +00:00
" 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",
2022-10-28 13:11:33 +00:00
" tracks_b=tracks_b,\n",
" mounting_holes=mounting_holes,\n",
2022-11-01 20:00:12 +00:00
" edge_cuts=edge_cuts,\n",
" components=components,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 160,
"metadata": {},
"outputs": [
{
"data": {
2023-02-04 11:17:28 +00:00
"image/png": "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
"text/plain": [
"<Figure size 1100x1100 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# plot the json\n",
"plot_json(json_result)"
2022-10-10 18:45:58 +00:00
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
2022-10-10 18:45:58 +00:00
"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"
2022-10-10 18:45:58 +00:00
},
"vscode": {
"interpreter": {
"hash": "97df458a008df4bb35f481235dd1a04940c0d868e77540a7386d9922f34deb97"
2022-10-10 18:45:58 +00:00
}
}
},
"nbformat": 4,
"nbformat_minor": 2
}