From 01d08d02bbdb7b3633e5012d877964e0f30a4606 Mon Sep 17 00:00:00 2001 From: Chris Greening Date: Sat, 8 Oct 2022 17:59:50 +0100 Subject: [PATCH] Add coil to jupyter notebook --- coil.ipynb | 547 -------------------------------------------- coil_plugin.py | 114 ++++----- coil_version2.ipynb | 158 ++++++++----- 3 files changed, 164 insertions(+), 655 deletions(-) delete mode 100644 coil.ipynb diff --git a/coil.ipynb b/coil.ipynb deleted file mode 100644 index cb266e7..0000000 --- a/coil.ipynb +++ /dev/null @@ -1,547 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import pandas as pd\n", - "import numpy as np\n", - "import matplotlib as plt\n", - "import scipy\n", - "from skspatial.objects import LineSegment\n", - "from enum import Enum" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "VIA_DIAM = 0.8\n", - "VIA_DRILL = 0.4\n", - "STATOR_HOLE_RADIUS = 4\n", - "TRACK_WIDTH = 0.2\n", - "TRACK_SPACING = 0.2\n", - "TURNS = 11\n", - "STATOR_RADIUS = 20\n", - "Layer = Enum(\"Layer\", \"FRONT BACK\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# create a square shape\n", - "# template = [(-1.5, -0.05), (1.5, -1.25), (1.5, 1.25), (-1.5, 0.05)]\n", - "# template = [(-6, -0.05), (6, -3), (6, 3), (-6, 0.05)]\n", - "# create a triangle\n", - "# template = [(-2, 0), (2, -3), (2, 3)]\n", - "# interpolate the shape using numpy\n", - "\n", - "# create a circle template\n", - "template = [\n", - " (np.cos(np.deg2rad(theta)), np.sin(np.deg2rad(theta)))\n", - " for theta in np.linspace(0, 360, 360)\n", - "]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# plot the template shape wrapping around to the first point\n", - "plt.pyplot.plot(\n", - " [x for x, y in template] + [template[0][0]],\n", - " [y for x, y in template] + [template[0][1]],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# apply Chaikin's algorithm to the template - corner cuttine\n", - "def chaikin(arr, iterations):\n", - " if iterations == 0:\n", - " return arr\n", - " l = len(arr)\n", - " smoothed = []\n", - " for i in range(l):\n", - " x1, y1 = arr[i]\n", - " x2, y2 = arr[(i + 1) % l]\n", - " smoothed.append([0.95 * x1 + 0.05 * x2, 0.95 * y1 + 0.05 * y2])\n", - " smoothed.append([0.05 * x1 + 0.95 * x2, 0.05 * y1 + 0.95 * y2])\n", - " return chaikin(smoothed, iterations - 1)\n", - "\n", - "\n", - "# template = chaikin(template, 2)\n", - "plt.pyplot.plot(\n", - " [x for x, y in template] + [template[0][0]],\n", - " [y for x, y in template] + [template[0][1]],\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# this speeds things up dramatically as we only have to compute the line intersections once\n", - "# there are probably much faster ways of doing this - we're just doing a brute force search\n", - "# for the intersections - consult algorithms from games for inspiration...\n", - "def get_template_point_cache(template):\n", - " # sweep a line from the origin through 360 degress times the number of turns in 1 degree increments\n", - " # and find the intersection points with the template shape\n", - " cache = {}\n", - " for angle in np.arange(0, 360 + 2, 2):\n", - " line = LineSegment(\n", - " np.array([0, 0]),\n", - " np.array(\n", - " [1000 * np.cos(np.deg2rad(angle)), 1000 * np.sin(np.deg2rad(angle))]\n", - " ),\n", - " )\n", - " for i in range(len(template)):\n", - " segment = LineSegment(\n", - " np.array(template[i]), np.array(template[(i + 1) % len(template)])\n", - " )\n", - " try:\n", - " intersection = line.intersect_line_segment(segment)\n", - " if intersection is not None:\n", - " cache[angle] = (intersection, segment)\n", - " except ValueError:\n", - " pass\n", - " return cache\n", - "\n", - "\n", - "# get the points in a coil shape\n", - "# Use reverse for bottom layer (basically flips the y coordinate so that the coil goes in the opposite direction)\n", - "# Also rotates the endpoints by 90 degress so that the exit point on the bottom layer is to the left hand side\n", - "def get_points(template, turns, spacing, layer=Layer.FRONT, cache=None):\n", - " if cache is None:\n", - " cache = get_template_point_cache(template)\n", - " points = []\n", - " for angle in np.arange(0, 360 * turns + 2, 2):\n", - " offset = spacing * angle / 360\n", - " if layer == Layer.BACK:\n", - " angle = angle + 180\n", - " intersection, segment = cache[angle % 360]\n", - " vector = np.array(segment.point_a) - np.array(segment.point_b)\n", - " normal = vector / np.linalg.norm(vector)\n", - " # rotate the vector 90 degrees\n", - " normal = np.array([-normal[1], normal[0]])\n", - " # move the intersection point along the normal vector by the spacing\n", - " coil_point = intersection + normal * offset\n", - " if layer == Layer.BACK:\n", - " points.append((coil_point[0], -coil_point[1]))\n", - " else:\n", - " points.append(coil_point)\n", - " return points" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "cache = get_template_point_cache(template)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "points_f = [(0, 0)] + get_points(\n", - " template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache\n", - ")\n", - "points_b = [(0, 0)] + get_points(\n", - " template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache\n", - ")\n", - "\n", - "COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n", - "\n", - "angle_A = 0\n", - "angle_B = 120\n", - "angle_C = 240\n", - "\n", - "# roate the points by the required angle\n", - "def rotate(points, angle):\n", - " return [\n", - " [\n", - " x * np.cos(np.deg2rad(angle)) - y * np.sin(np.deg2rad(angle)),\n", - " x * np.sin(np.deg2rad(angle)) + y * np.cos(np.deg2rad(angle)),\n", - " ]\n", - " for x, y in points\n", - " ]\n", - "\n", - "\n", - "# move the points out to the distance at the requited angle\n", - "def translate(points, distance, angle):\n", - " return [\n", - " [\n", - " x + distance * np.cos(np.deg2rad(angle)),\n", - " y + distance * np.sin(np.deg2rad(angle)),\n", - " ]\n", - " for x, y in points\n", - " ]\n", - "\n", - "\n", - "# flip the y coordinate\n", - "def flip(points):\n", - " return [[x, -y] for x, y in points]\n", - "\n", - "\n", - "# the main coils\n", - "coil_A_f = translate(rotate(points_f, angle_A), COIL_CENTER_RADIUS, angle_A)\n", - "coil_A_b = translate(rotate(points_b, angle_A), COIL_CENTER_RADIUS, angle_A)\n", - "\n", - "coil_B_f = translate(rotate(points_f, angle_B), COIL_CENTER_RADIUS, angle_B)\n", - "coil_B_b = translate(rotate(points_b, angle_B), COIL_CENTER_RADIUS, angle_B)\n", - "\n", - "coil_C_f = translate(rotate(points_f, angle_C), COIL_CENTER_RADIUS, angle_C)\n", - "coil_C_b = translate(rotate(points_b, angle_C), COIL_CENTER_RADIUS, angle_C)\n", - "\n", - "# the opposite coils - for more power!\n", - "angle_A_opp = angle_A + 180\n", - "angle_B_opp = angle_B + 180\n", - "angle_C_opp = angle_C + 180\n", - "\n", - "print(angle_A_opp, angle_B_opp, angle_C_opp)\n", - "\n", - "coil_A_opp_f = translate(\n", - " rotate(flip(points_f), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n", - ")\n", - "coil_A_opp_b = translate(\n", - " rotate(flip(points_b), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n", - ")\n", - "\n", - "coil_B_opp_f = translate(\n", - " rotate(flip(points_f), angle_B_opp), COIL_CENTER_RADIUS, angle_B_opp\n", - ")\n", - "coil_B_opp_b = translate(\n", - " rotate(flip(points_b), angle_B_opp), COIL_CENTER_RADIUS, angle_B_opp\n", - ")\n", - "\n", - "coil_C_opp_f = translate(\n", - " rotate(flip(points_f), angle_C_opp), COIL_CENTER_RADIUS, angle_C_opp\n", - ")\n", - "coil_C_opp_b = translate(\n", - " rotate(flip(points_b), angle_C_opp), COIL_CENTER_RADIUS, angle_C_opp\n", - ")\n", - "\n", - "# connect the front copper opposite coils together\n", - "common_connection_radius = STATOR_RADIUS - (TRACK_WIDTH + TRACK_SPACING)\n", - "common_coil_connections_b = [\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A_opp, angle_C_opp + 5, 5)\n", - "]\n", - "coil_A_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n", - " )\n", - ")\n", - "coil_B_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n", - " )\n", - ")\n", - "coil_C_opp_f.append(\n", - " (\n", - " common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n", - " common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n", - " )\n", - ")\n", - "\n", - "# connect coil A to it's opposite\n", - "connection_radius1 = STATOR_HOLE_RADIUS + (TRACK_SPACING)\n", - "connection_radius2 = connection_radius1 + (TRACK_SPACING + VIA_DIAM / 2)\n", - "# draw a 45 degree line from coil A at connection radius 1\n", - "# then connect up to connection radius 2\n", - "# draw a 45 degree line to the opposite coil\n", - "coil_A_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_A)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_A)),\n", - " )\n", - ")\n", - "coil_A_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_A_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_A_opp)),\n", - " )\n", - ")\n", - "a_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A, angle_A + 90 + 5, 5)\n", - "]\n", - "a_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_A + 90, angle_A + 180 + 5, 5)\n", - "]\n", - "a_connection_b.append(a_connection_f[0])\n", - "\n", - "coil_B_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_B)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_B)),\n", - " )\n", - ")\n", - "coil_B_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_B_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_B_opp)),\n", - " )\n", - ")\n", - "b_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_B, angle_B + 90 + 5, 5)\n", - "]\n", - "b_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_B + 90, angle_B + 180 + 5, 5)\n", - "]\n", - "b_connection_b.append(b_connection_f[0])\n", - "\n", - "coil_C_b.append(\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle_C)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle_C)),\n", - " )\n", - ")\n", - "coil_C_opp_b.append(\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle_C_opp)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle_C_opp)),\n", - " )\n", - ")\n", - "c_connection_b = [\n", - " (\n", - " connection_radius1 * np.cos(np.deg2rad(angle)),\n", - " connection_radius1 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_C, angle_C + 90 + 5, 5)\n", - "]\n", - "c_connection_f = [\n", - " (\n", - " connection_radius2 * np.cos(np.deg2rad(angle)),\n", - " connection_radius2 * np.sin(np.deg2rad(angle)),\n", - " )\n", - " for angle in np.arange(angle_C + 90, angle_C + 180 + 5, 5)\n", - "]\n", - "c_connection_b.append(c_connection_f[0])" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def create_track(points):\n", - " return [{\"x\": x, \"y\": y} for x, y in points]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# dump out the results to json\n", - "json_result = {\n", - " \"parameters\": {\n", - " \"trackWidth\": TRACK_WIDTH,\n", - " \"statorHoleRadius\": STATOR_HOLE_RADIUS,\n", - " \"viaDiameter\": VIA_DIAM,\n", - " \"viaDrillDiameter\": VIA_DRILL,\n", - " },\n", - " \"vias\": [\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A_opp)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B_opp)),\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C_opp)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_A_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_A_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_B_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_B_opp)),\n", - " },\n", - " {\n", - " \"x\": common_connection_radius * np.cos(np.deg2rad(angle_C_opp)),\n", - " \"y\": common_connection_radius * np.sin(np.deg2rad(angle_C_opp)),\n", - " },\n", - " # coil A connections\n", - " {\"x\": a_connection_f[0][0], \"y\": a_connection_f[0][1]},\n", - " {\"x\": a_connection_f[-1][0], \"y\": a_connection_f[-1][1]},\n", - " # coil B connections\n", - " {\"x\": b_connection_f[0][0], \"y\": b_connection_f[0][1]},\n", - " {\"x\": b_connection_f[-1][0], \"y\": b_connection_f[-1][1]},\n", - " # coil C connections\n", - " {\"x\": c_connection_f[0][0], \"y\": c_connection_f[0][1]},\n", - " {\"x\": c_connection_f[-1][0], \"y\": c_connection_f[-1][1]},\n", - " ],\n", - " \"silk\": [\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_A)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_A)),\n", - " \"text\": \"A\",\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_B)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_B)),\n", - " \"text\": \"B\",\n", - " },\n", - " {\n", - " \"x\": COIL_CENTER_RADIUS * np.cos(np.deg2rad(angle_C)),\n", - " \"y\": COIL_CENTER_RADIUS * np.sin(np.deg2rad(angle_C)),\n", - " \"text\": \"C\",\n", - " },\n", - " ],\n", - " \"tracks\": {\n", - " \"f\": [\n", - " create_track(points)\n", - " for points in [\n", - " coil_A_f,\n", - " coil_A_opp_f,\n", - " coil_B_f,\n", - " coil_B_opp_f,\n", - " coil_C_f,\n", - " coil_C_opp_f,\n", - " a_connection_f,\n", - " b_connection_f,\n", - " c_connection_f,\n", - " ]\n", - " ],\n", - " \"b\": [\n", - " create_track(points)\n", - " for points in [\n", - " coil_A_b,\n", - " coil_A_opp_b,\n", - " coil_B_b,\n", - " coil_B_opp_b,\n", - " coil_C_b,\n", - " coil_C_opp_b,\n", - " common_coil_connections_b,\n", - " a_connection_b,\n", - " b_connection_b,\n", - " c_connection_b,\n", - " ]\n", - " ],\n", - " },\n", - "}\n", - "\n", - "import json\n", - "\n", - "json.dump(json_result, open(\"coil.json\", \"w\"))\n", - "\n", - "\n", - "df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n", - "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", - "ax.axis(\"equal\")\n", - "df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n", - "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n", - "ax.axis(\"equal\")\n", - "\n", - "# plot all three coils on the same graph\n", - "# df = pd.DataFrame(coil_A, columns=['x', 'y'])\n", - "# ax = df.plot.line(x='x', y='y', label='Coil A', color='blue')\n", - "# ax.axis('equal')\n", - "# df = pd.DataFrame(coil_B, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil B', color='green')\n", - "# df = pd.DataFrame(coil_C, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil C', color='red')\n", - "\n", - "# df = pd.DataFrame(coil_A_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil A Opposite', color='blue')\n", - "# df = pd.DataFrame(coil_B_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil B Opposite', color='green')\n", - "# df = pd.DataFrame(coil_C_opposite, columns=['x', 'y'])\n", - "# df.plot.line(x='x', y='y', ax=ax, label='Coil C Opposite', color='red')" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3.10.7 ('venv': 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.10.7" - }, - "vscode": { - "interpreter": { - "hash": "1ce20143987840b9786ebb5907032c9c3a8efacbb887dbb0ebc4934f2ad26cb3" - } - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/coil_plugin.py b/coil_plugin.py index df632c0..a1e23c9 100644 --- a/coil_plugin.py +++ b/coil_plugin.py @@ -1,5 +1,6 @@ import pcbnew import json +import wx def create_tracks(board, group, net, layer, thickness, coords): @@ -31,65 +32,72 @@ class CoilPlugin(pcbnew.ActionPlugin): # self.icon_file_name = os.path.join(os.path.dirname(__file__), 'simple_plugin.png') # Optional, defaults to "" def Run(self): - board = pcbnew.GetBoard() - # load up the JSON with the coil parameters - coil_data = json.load( - open( - "/Users/chrisgreening/Work/projects/pcb_motor/kicad-coil-plugins/coil.json" - ) - ) - # parameters - track_width = coil_data["parameters"]["trackWidth"] - stator_hole_radius = coil_data["parameters"]["statorHoleRadius"] - via_diameter = coil_data["parameters"]["viaDiameter"] - via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"] + # launch a file picker dialog to get the coil file + dialog = wx.FileDialog(None, "Choose a coil file", "", "", "*.json", wx.FD_OPEN) + if dialog.ShowModal() == wx.ID_OK: + # read the file + with open(dialog.GetPath(), "r") as f: + board = pcbnew.GetBoard() + # load up the JSON with the coil parameters + coil_data = json.load(f) + # parameters + track_width = coil_data["parameters"]["trackWidth"] + stator_hole_radius = coil_data["parameters"]["statorHoleRadius"] + via_diameter = coil_data["parameters"]["viaDiameter"] + via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"] - # put everything in a group to make it easier to manage - pcb_group = pcbnew.PCB_GROUP(board) - # board.Add(pcb_group) + # put everything in a group to make it easier to manage + pcb_group = pcbnew.PCB_GROUP(board) + # board.Add(pcb_group) - # create the center hole - arc = pcbnew.PCB_SHAPE(board) - arc.SetShape(pcbnew.SHAPE_T_ARC) - arc.SetStart(pcbnew.wxPointMM(stator_hole_radius, 0)) - arc.SetCenter(pcbnew.wxPointMM(0, 0)) - arc.SetArcAngleAndEnd(0, False) - arc.SetLayer(pcbnew.Edge_Cuts) - arc.SetWidth(int(0.1 * pcbnew.IU_PER_MM)) - board.Add(arc) - # pcb_group.AddItem(arc) + # create the center hole + arc = pcbnew.PCB_SHAPE(board) + arc.SetShape(pcbnew.SHAPE_T_ARC) + arc.SetStart(pcbnew.wxPointMM(stator_hole_radius, 0)) + arc.SetCenter(pcbnew.wxPointMM(0, 0)) + arc.SetArcAngleAndEnd(0, False) + arc.SetLayer(pcbnew.Edge_Cuts) + arc.SetWidth(int(0.1 * pcbnew.IU_PER_MM)) + board.Add(arc) + # pcb_group.AddItem(arc) - # create tracks - for track in coil_data["tracks"]["f"]: - # find the matching net for the track - net = board.FindNet("coils") - if net is None: - raise "Net not found: {}".format(track["net"]) - create_tracks(board, pcb_group, net, pcbnew.F_Cu, track_width, track) + # create tracks + for track in coil_data["tracks"]["f"]: + # find the matching net for the track + net = board.FindNet("coils") + if net is None: + raise "Net not found: {}".format(track["net"]) + create_tracks( + board, pcb_group, net, pcbnew.F_Cu, track_width, track + ) - for track in coil_data["tracks"]["b"]: - create_tracks(board, pcb_group, net, pcbnew.B_Cu, track_width, track) + for track in coil_data["tracks"]["b"]: + create_tracks( + board, pcb_group, net, pcbnew.B_Cu, track_width, track + ) - # create the vias - for via in coil_data["vias"]: - pcb_via = pcbnew.PCB_VIA(board) - pcb_via.SetPosition(pcbnew.wxPointMM(float(via["x"]), float(via["y"]))) - pcb_via.SetWidth(int(via_diameter * 1e6)) - pcb_via.SetDrill(int(via_drill_diameter * 1e6)) - pcb_via.SetNetCode(net.GetNetCode()) - board.Add(pcb_via) - # pcb_group.AddItem(pcb_via) + # create the vias + for via in coil_data["vias"]: + pcb_via = pcbnew.PCB_VIA(board) + pcb_via.SetPosition( + pcbnew.wxPointMM(float(via["x"]), float(via["y"])) + ) + pcb_via.SetWidth(int(via_diameter * 1e6)) + pcb_via.SetDrill(int(via_drill_diameter * 1e6)) + pcb_via.SetNetCode(net.GetNetCode()) + board.Add(pcb_via) + # pcb_group.AddItem(pcb_via) - # create any silk screen - for text in coil_data["silk"]: - pcb_txt = pcbnew.PCB_TEXT(board) - pcb_txt.SetText(text["text"]) - pcb_txt.SetPosition(pcbnew.wxPointMM(text["x"], text["y"])) - pcb_txt.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER) - pcb_txt.SetTextSize(pcbnew.wxSize(5000000, 5000000)) - pcb_txt.SetLayer(pcbnew.F_SilkS) - board.Add(pcb_txt) - # pcb_group.AddItem(pcb_txt) + # create any silk screen + for text in coil_data["silk"]: + pcb_txt = pcbnew.PCB_TEXT(board) + pcb_txt.SetText(text["text"]) + pcb_txt.SetPosition(pcbnew.wxPointMM(text["x"], text["y"])) + pcb_txt.SetHorizJustify(pcbnew.GR_TEXT_HJUSTIFY_CENTER) + pcb_txt.SetTextSize(pcbnew.wxSize(5000000, 5000000)) + pcb_txt.SetLayer(pcbnew.F_SilkS) + board.Add(pcb_txt) + # pcb_group.AddItem(pcb_txt) CoilPlugin().register() # Instantiate and register to Pcbnew]) diff --git a/coil_version2.ipynb b/coil_version2.ipynb index 12cda28..0dd3c54 100644 --- a/coil_version2.ipynb +++ b/coil_version2.ipynb @@ -29,9 +29,17 @@ "TRACK_SPACING = 0.2\n", "TURNS = 11\n", "STATOR_RADIUS = 20\n", + "USE_SPIRAL = False\n", "Layer = Enum(\"Layer\", \"FRONT BACK\")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Arbitrary Coil Generation" + ] + }, { "cell_type": "code", "execution_count": null, @@ -200,23 +208,6 @@ " return chaikin(smoothed, iterations - 1)" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "points = get_points(template, 10, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, None)\n", - "optimized_points = optimize_points(points)\n", - "df = pd.DataFrame(points, columns=[\"x\", \"y\"])\n", - "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", - "ax.axis(\"equal\")\n", - "\n", - "df_optim = pd.DataFrame(optimized_points, columns=[\"x\", \"y\"])\n", - "df_optim.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"green\", ax=ax)\n", - "print(len(df_optim), len(df))" - ] - }, { "cell_type": "code", "execution_count": null, @@ -232,15 +223,84 @@ "metadata": {}, "outputs": [], "source": [ - "vias = []\n", + "if not USE_SPIRAL:\n", + " points_f = get_points(\n", + " template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache\n", + " )\n", + " points_b = get_points(\n", + " template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache\n", + " )\n", "\n", - "points_f = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT, cache)\n", - "points_b = get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH, Layer.BACK, cache)\n", - "\n", - "points_f = [(0, 0)] + chaikin(optimize_points(points_f), 3)\n", - "points_b = [(0, 0)] + chaikin(optimize_points(points_b), 3)\n", - "print(\"Track points\", len(points_f), len(points_b))\n", + " points_f = [(0, 0)] + chaikin(optimize_points(points_f), 3)\n", + " points_b = [(0, 0)] + chaikin(optimize_points(points_b), 3)\n", + " print(\"Track points\", len(points_f), len(points_b))\n", + "else:\n", + " print(\"Using spiral\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Basic Spiral Coil Generation" + ] + }, + { + "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 USE_SPIRAL:\n", + " points_f = get_spiral(\n", + " TURNS, VIA_DIAM + TRACK_SPACING, TRACK_SPACING + TRACK_WIDTH, Layer.FRONT\n", + " )\n", + " points_b = get_spiral(\n", + " TURNS, VIA_DIAM + 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))\n", + "else:\n", + " print(\"Using template\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Generate PCB Layout" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ "# calculat 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", @@ -254,8 +314,16 @@ " 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)\n", - "\n", + "print(\"Total length back\", total_length_back)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "vias = []\n", "\n", "COIL_CENTER_RADIUS = STATOR_RADIUS / 2 + 1.5\n", "\n", @@ -327,8 +395,6 @@ "angle_B_opp = angle_B + 180\n", "angle_C_opp = angle_C + 180\n", "\n", - "print(angle_A_opp, angle_B_opp, angle_C_opp)\n", - "\n", "coil_A_opp_f = translate(\n", " rotate(flip(points_f), angle_A_opp), COIL_CENTER_RADIUS, angle_A_opp\n", ")\n", @@ -428,15 +494,9 @@ "outputs": [], "source": [ "def create_track(points):\n", - " return [{\"x\": x, \"y\": y} for x, y in points]" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ + " return [{\"x\": x, \"y\": y} for x, y in points]\n", + "\n", + "\n", "# dump out the results to json\n", "json_result = {\n", " \"parameters\": {\n", @@ -504,24 +564,12 @@ "json.dump(json_result, open(\"coil.json\", \"w\"))\n", "\n", "\n", - "df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n", - "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", - "ax.axis(\"equal\")\n", - "df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n", - "ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n", - "ax.axis(\"equal\")\n", - "\n", - "# print the number of segments in each front track\n", - "for track in json_result[\"tracks\"][\"f\"]:\n", - " print(len(track))\n", - "\n", - "# print the number of segments in each back track\n", - "for track in json_result[\"tracks\"][\"b\"]:\n", - " print(len(track))\n", - "\n", - "# print the total number of segments in both front and back tracks\n", - "print(sum([len(track) for track in json_result[\"tracks\"][\"f\"]]))\n", - "print(sum([len(track) for track in json_result[\"tracks\"][\"b\"]]))\n", + "# df = pd.DataFrame(coil_A_f, columns=[\"x\", \"y\"])\n", + "# ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil A\", color=\"blue\")\n", + "# ax.axis(\"equal\")\n", + "# df = pd.DataFrame(coil_A_b, columns=[\"x\", \"y\"])\n", + "# ax = df.plot.line(x=\"x\", y=\"y\", label=\"Coil B\", color=\"green\")\n", + "# ax.axis(\"equal\")\n", "\n", "\n", "# plot the back tracks\n",