From f11f403e9f2591bc963dfb8715348a1cb7bc5018 Mon Sep 17 00:00:00 2001 From: Chris Greening Date: Tue, 11 Oct 2022 11:14:28 +0100 Subject: [PATCH] Refactor common functions out from notebooks --- coil_generator-12.ipynb | 408 ++++-------------- ..._generator.ipynb => coil_generator-6.ipynb | 300 +++---------- coil_plugin.py | 6 +- helpers.py | 112 +++++ pcb_json.py | 140 ++++++ requirements.txt | 2 +- 6 files changed, 407 insertions(+), 561 deletions(-) rename coil_generator.ipynb => coil_generator-6.ipynb (67%) create mode 100644 helpers.py create mode 100644 pcb_json.py diff --git a/coil_generator-12.ipynb b/coil_generator-12.ipynb index 5271a70..daf72de 100644 --- a/coil_generator-12.ipynb +++ b/coil_generator-12.ipynb @@ -9,10 +9,27 @@ "import pandas as pd\n", "import numpy as np\n", "\n", - "# import matplotlib as plt\n", "import matplotlib.pyplot as plt\n", - "import scipy\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", + " create_via,\n", + " create_pad,\n", + " create_track,\n", + " create_silk,\n", + " create_silk,\n", + " optimize_points,\n", + " chaikin,\n", + ")\n", + "from pcb_json import dump_json, plot_json\n", + "\n", "from enum import Enum\n", "\n", "Layer = Enum(\"Layer\", \"FRONT BACK\")" @@ -26,98 +43,29 @@ "source": [ "VIA_DIAM = 0.8\n", "VIA_DRILL = 0.4\n", + "# this is for a 1.27mm pitch pin\n", + "PAD_DIAM = 1.0\n", + "PAD_DRILL = 0.65\n", + "\n", + "# PCB Edge size\n", + "STATOR_RADIUS = 18\n", "STATOR_HOLE_RADIUS = 5\n", + "\n", + "# Track width and spacing\n", "TRACK_WIDTH = 0.127\n", "TRACK_SPACING = 0.127\n", + "\n", + "# Coil params\n", "TURNS = 9\n", - "STATOR_RADIUS = 18\n", - "PIN_DIAM = 1.7\n", "COIL_CENTER_RADIUS = 11.5\n", "COIL_VIA_RADIUS = 12.5\n", + "\n", "# where to place the pins\n", - "CONNECTION_PINS_RADIUS = 16\n", + "CONNECTION_PINS_RADIUS = 16.5\n", + "\n", "USE_SPIRAL = False" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# get the point on an arc at the given angle\n", - "def get_arc_point(angle, radius):\n", - " return (\n", - " radius * np.cos(np.deg2rad(angle)),\n", - " radius * np.sin(np.deg2rad(angle)),\n", - " )\n", - "\n", - "\n", - "# draw an arc\n", - "def draw_arc(start_angle, end_angle, radius, step=10):\n", - " points = []\n", - " for angle in np.arange(start_angle, end_angle + step, step):\n", - " x = radius * np.cos(np.deg2rad(angle))\n", - " y = radius * np.sin(np.deg2rad(angle))\n", - " points.append((x, y))\n", - " return points\n", - "\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_y(points):\n", - " return [[x, -y] for x, y in points]\n", - "\n", - "\n", - "def flip_x(points):\n", - " return [[-x, y] for x, y in points]\n", - "\n", - "\n", - "def create_pad(radius, angle, name):\n", - " return {\n", - " \"x\": radius * np.cos(np.deg2rad(angle)),\n", - " \"y\": radius * np.sin(np.deg2rad(angle)),\n", - " \"name\": name,\n", - " }\n", - "\n", - "\n", - "def create_silk(point, text):\n", - " return {\n", - " \"x\": point[0],\n", - " \"y\": point[1],\n", - " \"text\": text,\n", - " }\n", - "\n", - "\n", - "def create_via(point):\n", - " return {\"x\": point[0], \"y\": point[1]}\n", - "\n", - "\n", - "def create_track(points):\n", - " return [{\"x\": x, \"y\": y} for x, y in points]" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -222,46 +170,7 @@ " coil_points.append(coil_point2)\n", "\n", " template_index = template_index + 1\n", - " return coil_points\n", - "\n", - "\n", - "def optimize_points(points):\n", - " # follow the line and remove points that are in the same direction as the previous poin\n", - " # keep doing this until the direction changes significantly\n", - " # this is a very simple optimization that removes a lot of points\n", - " # it's not perfect but it's a good start\n", - " optimized_points = []\n", - " for i in range(len(points)):\n", - " if i == 0:\n", - " optimized_points.append(points[i])\n", - " else:\n", - " vector1 = np.array(points[i]) - np.array(points[i - 1])\n", - " vector2 = np.array(points[(i + 1) % len(points)]) - np.array(points[i])\n", - " length1 = np.linalg.norm(vector1)\n", - " length2 = np.linalg.norm(vector2)\n", - " if length1 > 0 and length2 > 0:\n", - " dot = np.dot(vector1, vector2) / (length1 * length2)\n", - " # clamp dot between -1 and 1\n", - " dot = max(-1, min(1, dot))\n", - " angle = np.arccos(dot)\n", - " if angle > np.deg2rad(5):\n", - " optimized_points.append(points[i])\n", - " print(\"Optimised from {} to {} points\".format(len(points), len(optimized_points)))\n", - " return optimized_points\n", - "\n", - "\n", - "def chaikin(points, iterations):\n", - " if iterations == 0:\n", - " return points\n", - " l = len(points)\n", - " smoothed = []\n", - " for i in range(l - 1):\n", - " x1, y1 = points[i]\n", - " x2, y2 = points[i + 1]\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", - " smoothed.append(points[l - 1])\n", - " return chaikin(smoothed, iterations - 1)" + " return coil_points" ] }, { @@ -270,83 +179,28 @@ "metadata": {}, "outputs": [], "source": [ - "if not USE_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", + "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(flip_x(get_points(template_f, TURNS, TRACK_SPACING + TRACK_WIDTH))),\n", + " 2,\n", + ")\n", + "points_b = chaikin(\n", + " optimize_points(get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH)), 2\n", + ")\n", "\n", - " points_f = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_f\n", - " points_b = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_b\n", + "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", + "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))\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 / 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 = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_f\n", - " points_b = [(COIL_VIA_RADIUS - COIL_CENTER_RADIUS, 0)] + points_b\n", - " print(\"Track points\", len(points_f), len(points_b))\n", - "else:\n", - " print(\"Using template\")" + "print(\"Track points\", len(points_f), len(points_b))" ] }, { @@ -517,14 +371,16 @@ "vias.append(create_via(get_arc_point(8 * 360 / 12, outer_connection_radius_C)))\n", "\n", "# create pads for the input\n", - "pin_radius = STATOR_RADIUS - TRACK_SPACING - PIN_DIAM / 2\n", - "pads.append(create_pad(pin_radius, 0, \"A\"))\n", - "pads.append(create_pad(pin_radius, 1 * 360 / 12, \"B\"))\n", - "pads.append(create_pad(pin_radius, 2 * 360 / 12, \"C\"))\n", + "for angle in range(0, 360, 15):\n", + " pads.append(create_pad(CONNECTION_PINS_RADIUS, angle, \"A\"))\n", "\n", - "coils_f[0].append(get_arc_point(0, pin_radius))\n", - "coils_f[1].append(get_arc_point(1 * 360 / 12, pin_radius))\n", - "coils_f[2].append(get_arc_point(2 * 360 / 12, pin_radius))" + "# coils_f[0].append(get_arc_point(0, CONNECTION_PINS_RADIUS))\n", + "# coils_f[1].append(get_arc_point(1 * 360 / 12, CONNECTION_PINS_RADIUS))\n", + "# coils_f[2].append(get_arc_point(2 * 360 / 12, CONNECTION_PINS_RADIUS))\n", + "\n", + "# coils_f[0].append(get_arc_point(15, CONNECTION_PINS_RADIUS))\n", + "# coils_f[1].append(get_arc_point(15 + 1 * 360 / 12, CONNECTION_PINS_RADIUS))\n", + "# coils_f[2].append(get_arc_point(15 + 2 * 360 / 12, CONNECTION_PINS_RADIUS))" ] }, { @@ -533,122 +389,32 @@ "metadata": {}, "outputs": [], "source": [ - "# dump out the results to json\n", - "json_result = {\n", - " \"parameters\": {\n", - " \"trackWidth\": TRACK_WIDTH,\n", - " \"statorHoleRadius\": STATOR_HOLE_RADIUS,\n", - " \"statorRadius\": STATOR_RADIUS,\n", - " \"viaDiameter\": VIA_DIAM,\n", - " \"viaDrillDiameter\": VIA_DRILL,\n", - " },\n", - " \"vias\": vias,\n", - " \"pads\": pads,\n", - " \"silk\": silk,\n", - " \"tracks\": {\n", - " \"f\": [create_track(points) for points in tracks_f],\n", - " \"b\": [create_track(points) for points in tracks_b],\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", - "\n", - "# plot the back tracks\n", - "ax = None\n", - "for track in json_result[\"tracks\"][\"b\"]:\n", - " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", - " ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\", ax=ax)\n", - " ax.axis(\"equal\")\n", - "\n", - "# plot the front tracks\n", - "for track in json_result[\"tracks\"][\"f\"]:\n", - " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", - " ax = df.plot.line(x=\"x\", y=\"y\", color=\"red\", ax=ax)\n", - " ax.axis(\"equal\")\n", - "\n", - "# hide the legend\n", - "ax.legend().set_visible(False)\n", - "# make the plot bigger\n", - "ax.figure.set_size_inches(10, 10)\n", - "\n", - "# plot the vias\n", - "for via in json_result[\"vias\"]:\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (via[\"x\"], via[\"y\"]),\n", - " radius=VIA_DIAM / 2,\n", - " fill=True,\n", - " color=\"black\",\n", - " )\n", - " )\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (via[\"x\"], via[\"y\"]),\n", - " radius=VIA_DRILL / 2,\n", - " fill=True,\n", - " color=\"white\",\n", - " )\n", - " )\n", - "\n", - "# plot the edge cuts\n", - "ax.add_patch(\n", - " plt.Circle(\n", - " (0, 0),\n", - " radius=STATOR_RADIUS,\n", - " fill=False,\n", - " color=\"yellow\",\n", - " )\n", - ")\n", - "ax.add_patch(\n", - " plt.Circle(\n", - " (0, 0),\n", - " radius=STATOR_HOLE_RADIUS,\n", - " fill=False,\n", - " color=\"yellow\",\n", - " )\n", - ")\n", - "\n", - "# plot the pads\n", - "for pad in json_result[\"pads\"]:\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (pad[\"x\"], pad[\"y\"]),\n", - " radius=1.7 / 2,\n", - " fill=True,\n", - " color=\"yellow\",\n", - " )\n", - " )\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (pad[\"x\"], pad[\"y\"]),\n", - " radius=1.0 / 2,\n", - " fill=True,\n", - " color=\"white\",\n", - " )\n", - " )\n", - "\n", - "# plot the silk\n", - "for silk in json_result[\"silk\"]:\n", - " ax.text(\n", - " silk[\"x\"],\n", - " silk[\"y\"],\n", - " silk[\"text\"],\n", - " horizontalalignment=\"center\",\n", - " verticalalignment=\"center\",\n", - " color=\"black\",\n", - " fontsize=50,\n", - " )" + "# dump out the json version\n", + "json_result = dump_json(\n", + " \"coils_12.json\",\n", + " STATOR_RADIUS,\n", + " STATOR_HOLE_RADIUS,\n", + " TRACK_WIDTH,\n", + " PAD_DIAM,\n", + " PAD_DRILL,\n", + " VIA_DIAM,\n", + " VIA_DRILL,\n", + " vias,\n", + " pads,\n", + " silk,\n", + " tracks_f,\n", + " tracks_b,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot the json\n", + "plot_json(json_result)" ] } ], diff --git a/coil_generator.ipynb b/coil_generator-6.ipynb similarity index 67% rename from coil_generator.ipynb rename to coil_generator-6.ipynb index 1e8251c..dc59e98 100644 --- a/coil_generator.ipynb +++ b/coil_generator-6.ipynb @@ -11,8 +11,24 @@ "\n", "# import matplotlib as plt\n", "import matplotlib.pyplot as plt\n", - "import scipy\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", + " create_pad,\n", + " create_silk,\n", + " create_via,\n", + ")\n", + "from pcb_json import dump_json, plot_json\n", + "\n", "from enum import Enum\n", "\n", "Layer = Enum(\"Layer\", \"FRONT BACK\")" @@ -26,72 +42,27 @@ "source": [ "VIA_DIAM = 0.8\n", "VIA_DRILL = 0.4\n", + "# this is for a 1.27mm pitch pin\n", + "PAD_DIAM = 1.0\n", + "PAD_DRILL = 0.65\n", + "\n", + "# PCB Edge size\n", + "STATOR_RADIUS = 18\n", "STATOR_HOLE_RADIUS = 5\n", + "\n", + "# Track width and spacing\n", "TRACK_WIDTH = 0.127\n", "TRACK_SPACING = 0.127\n", + "\n", + "# Coil params\n", "TURNS = 18\n", - "STATOR_RADIUS = 18\n", "COIL_CENTER_RADIUS = 11.5\n", "# where to place the pins\n", - "CONNECTION_PINS_RADIUS = 16\n", + "CONNECTION_PINS_RADIUS = 16.5\n", + "\n", "USE_SPIRAL = False" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "# get the point on an arc at the given angle\n", - "def get_arc_point(angle, radius):\n", - " return (\n", - " radius * np.cos(np.deg2rad(angle)),\n", - " radius * np.sin(np.deg2rad(angle)),\n", - " )\n", - "\n", - "\n", - "# draw an arc\n", - "def draw_arc(start_angle, end_angle, radius, step=10):\n", - " points = []\n", - " for angle in np.arange(start_angle, end_angle + step, step):\n", - " x = radius * np.cos(np.deg2rad(angle))\n", - " y = radius * np.sin(np.deg2rad(angle))\n", - " points.append((x, y))\n", - " return points\n", - "\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_y(points):\n", - " return [[x, -y] for x, y in points]\n", - "\n", - "\n", - "def flip_x(points):\n", - " return [[-x, y] for x, y in points]" - ] - }, { "cell_type": "markdown", "metadata": {}, @@ -198,46 +169,7 @@ " coil_points.append(coil_point2)\n", "\n", " template_index = template_index + 1\n", - " return coil_points\n", - "\n", - "\n", - "def optimize_points(points):\n", - " # follow the line and remove points that are in the same direction as the previous poin\n", - " # keep doing this until the direction changes significantly\n", - " # this is a very simple optimization that removes a lot of points\n", - " # it's not perfect but it's a good start\n", - " optimized_points = []\n", - " for i in range(len(points)):\n", - " if i == 0:\n", - " optimized_points.append(points[i])\n", - " else:\n", - " vector1 = np.array(points[i]) - np.array(points[i - 1])\n", - " vector2 = np.array(points[(i + 1) % len(points)]) - np.array(points[i])\n", - " length1 = np.linalg.norm(vector1)\n", - " length2 = np.linalg.norm(vector2)\n", - " if length1 > 0 and length2 > 0:\n", - " dot = np.dot(vector1, vector2) / (length1 * length2)\n", - " # clamp dot between -1 and 1\n", - " dot = max(-1, min(1, dot))\n", - " angle = np.arccos(dot)\n", - " if angle > np.deg2rad(5):\n", - " optimized_points.append(points[i])\n", - " print(\"Optimised from {} to {} points\".format(len(points), len(optimized_points)))\n", - " return optimized_points\n", - "\n", - "\n", - "def chaikin(points, iterations):\n", - " if iterations == 0:\n", - " return points\n", - " l = len(points)\n", - " smoothed = []\n", - " for i in range(l - 1):\n", - " x1, y1 = points[i]\n", - " x2, y2 = points[i + 1]\n", - " smoothed.append([0.9 * x1 + 0.1 * x2, 0.9 * y1 + 0.1 * y2])\n", - " smoothed.append([0.1 * x1 + 0.9 * x2, 0.1 * y1 + 0.9 * y2])\n", - " smoothed.append(points[l - 1])\n", - " return chaikin(smoothed, iterations - 1)" + " return coil_points" ] }, { @@ -369,15 +301,6 @@ "angle_B = 120\n", "angle_C = 240\n", "\n", - "\n", - "def create_pad(radius, angle, name):\n", - " return {\n", - " \"x\": radius * np.cos(np.deg2rad(angle)),\n", - " \"y\": radius * np.sin(np.deg2rad(angle)),\n", - " \"name\": name,\n", - " }\n", - "\n", - "\n", "# create the pads at CONNECTION_PINS radius - 2 for each of the coils, A, B and C\n", "pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_A - 30, \"A\"))\n", "pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_A + 30, \"A\"))\n", @@ -388,11 +311,6 @@ "pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_C - 30, \"C\"))\n", "pads.append(create_pad(CONNECTION_PINS_RADIUS, angle_C + 30, \"C\"))\n", "\n", - "\n", - "def create_via(point):\n", - " return {\"x\": point[0], \"y\": point[1]}\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", @@ -527,7 +445,13 @@ "\n", "vias.append(create_via(a_connection_f[-1]))\n", "vias.append(create_via(b_connection_f[-1]))\n", - "vias.append(create_via(c_connection_f[-1]))" + "vias.append(create_via(c_connection_f[-1]))\n", + "\n", + "silk = [\n", + " create_silk(get_arc_point(angle_A, COIL_CENTER_RADIUS), \"A\"),\n", + " create_silk(get_arc_point(angle_B, COIL_CENTER_RADIUS), \"B\"),\n", + " create_silk(get_arc_point(angle_C, COIL_CENTER_RADIUS), \"C\"),\n", + "]" ] }, { @@ -536,130 +460,32 @@ "metadata": {}, "outputs": [], "source": [ - "def create_track(points):\n", - " 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", - " \"trackWidth\": TRACK_WIDTH,\n", - " \"statorHoleRadius\": STATOR_HOLE_RADIUS,\n", - " \"statorRadius\": STATOR_RADIUS,\n", - " \"viaDiameter\": VIA_DIAM,\n", - " \"viaDrillDiameter\": VIA_DRILL,\n", - " },\n", - " \"vias\": vias,\n", - " \"pads\": pads,\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\": [create_track(points) for points in tracks_f],\n", - " \"b\": [create_track(points) for points in tracks_b],\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", - "\n", - "# plot the back tracks\n", - "ax = None\n", - "for track in json_result[\"tracks\"][\"b\"]:\n", - " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", - " ax = df.plot.line(x=\"x\", y=\"y\", color=\"blue\", ax=ax)\n", - " ax.axis(\"equal\")\n", - "\n", - "# plot the front tracks\n", - "for track in json_result[\"tracks\"][\"f\"]:\n", - " df = pd.DataFrame(track, columns=[\"x\", \"y\"])\n", - " ax = df.plot.line(x=\"x\", y=\"y\", color=\"red\", ax=ax)\n", - " ax.axis(\"equal\")\n", - "\n", - "# hide the legend\n", - "ax.legend().set_visible(False)\n", - "# make the plot bigger\n", - "ax.figure.set_size_inches(10, 10)\n", - "\n", - "# plot the vias\n", - "for via in json_result[\"vias\"]:\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (via[\"x\"], via[\"y\"]),\n", - " radius=VIA_DIAM / 2,\n", - " fill=True,\n", - " color=\"black\",\n", - " )\n", - " )\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (via[\"x\"], via[\"y\"]),\n", - " radius=VIA_DRILL / 2,\n", - " fill=True,\n", - " color=\"white\",\n", - " )\n", - " )\n", - "\n", - "# plot the edge cuts\n", - "ax.add_patch(\n", - " plt.Circle(\n", - " (0, 0),\n", - " radius=STATOR_RADIUS,\n", - " fill=False,\n", - " color=\"yellow\",\n", - " )\n", - ")\n", - "ax.add_patch(\n", - " plt.Circle(\n", - " (0, 0),\n", - " radius=STATOR_HOLE_RADIUS,\n", - " fill=False,\n", - " color=\"yellow\",\n", - " )\n", - ")\n", - "\n", - "# plot the pads\n", - "for pad in json_result[\"pads\"]:\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (pad[\"x\"], pad[\"y\"]),\n", - " radius=1.7 / 2,\n", - " fill=True,\n", - " color=\"yellow\",\n", - " )\n", - " )\n", - " ax.add_patch(\n", - " plt.Circle(\n", - " (pad[\"x\"], pad[\"y\"]),\n", - " radius=1.0 / 2,\n", - " fill=True,\n", - " color=\"white\",\n", - " )\n", - " )" + "# dump out the json version\n", + "json_result = dump_json(\n", + " \"coils_6.json\",\n", + " STATOR_RADIUS,\n", + " STATOR_HOLE_RADIUS,\n", + " TRACK_WIDTH,\n", + " PAD_DIAM,\n", + " PAD_DRILL,\n", + " VIA_DIAM,\n", + " VIA_DRILL,\n", + " vias,\n", + " pads,\n", + " silk,\n", + " tracks_f,\n", + " tracks_b,\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# plot the json\n", + "plot_json(json_result)" ] } ], diff --git a/coil_plugin.py b/coil_plugin.py index c3748ee..121d9be 100644 --- a/coil_plugin.py +++ b/coil_plugin.py @@ -44,6 +44,8 @@ class CoilPlugin(pcbnew.ActionPlugin): track_width = coil_data["parameters"]["trackWidth"] stator_hole_radius = coil_data["parameters"]["statorHoleRadius"] stator_radius = coil_data["parameters"]["statorRadius"] + pad_diameter = coil_data["parameters"]["padDiameter"] + pad_drill = coil_data["parameters"]["padDrillDiameter"] via_diameter = coil_data["parameters"]["viaDiameter"] via_drill_diameter = coil_data["parameters"]["viaDrillDiameter"] @@ -86,11 +88,11 @@ class CoilPlugin(pcbnew.ActionPlugin): module.SetPosition(pcbnew.wxPointMM(pad["x"], pad["y"])) board.Add(module) pcb_pad = pcbnew.PAD(module) - pcb_pad.SetSize(pcbnew.wxSizeMM(1.7, 1.7)) + pcb_pad.SetSize(pcbnew.wxSizeMM(pad_diameter, pad_diameter)) pcb_pad.SetShape(pcbnew.PAD_SHAPE_CIRCLE) pcb_pad.SetAttribute(pcbnew.PAD_ATTRIB_PTH) pcb_pad.SetLayerSet(pcb_pad.PTHMask()) - pcb_pad.SetDrillSize(pcbnew.wxSizeMM(1.0, 1.0)) + pcb_pad.SetDrillSize(pcbnew.wxSizeMM(pad_drill, pad_drill)) pcb_pad.SetPosition(pcbnew.wxPointMM(pad["x"], pad["y"])) pcb_pad.SetNetCode(net.GetNetCode()) module.Add(pcb_pad) diff --git a/helpers.py b/helpers.py new file mode 100644 index 0000000..301feed --- /dev/null +++ b/helpers.py @@ -0,0 +1,112 @@ +import numpy as np + +# get the point on an arc at the given angle +def get_arc_point(angle, radius): + return ( + radius * np.cos(np.deg2rad(angle)), + radius * np.sin(np.deg2rad(angle)), + ) + + +# draw an arc +def draw_arc(start_angle, end_angle, radius, step=10): + points = [] + for angle in np.arange(start_angle, end_angle + step, step): + x = radius * np.cos(np.deg2rad(angle)) + y = radius * np.sin(np.deg2rad(angle)) + points.append((x, y)) + return points + + +# roate the points by the required angle +def rotate(points, angle): + return [ + [ + x * np.cos(np.deg2rad(angle)) - y * np.sin(np.deg2rad(angle)), + x * np.sin(np.deg2rad(angle)) + y * np.cos(np.deg2rad(angle)), + ] + for x, y in points + ] + + +# move the points out to the distance at the requited angle +def translate(points, distance, angle): + return [ + [ + x + distance * np.cos(np.deg2rad(angle)), + y + distance * np.sin(np.deg2rad(angle)), + ] + for x, y in points + ] + + +# flip the y coordinate +def flip_y(points): + return [[x, -y] for x, y in points] + + +def flip_x(points): + return [[-x, y] for x, y in points] + + +def create_pad(radius, angle, name): + return { + "x": radius * np.cos(np.deg2rad(angle)), + "y": radius * np.sin(np.deg2rad(angle)), + "name": name, + } + + +def create_silk(point, text): + return { + "x": point[0], + "y": point[1], + "text": text, + } + + +def create_via(point): + return {"x": point[0], "y": point[1]} + + +def create_track(points): + return [{"x": x, "y": y} for x, y in points] + + +def optimize_points(points): + # follow the line and remove points that are in the same direction as the previous poin + # keep doing this until the direction changes significantly + # this is a very simple optimization that removes a lot of points + # it's not perfect but it's a good start + optimized_points = [] + for i in range(len(points)): + if i == 0: + optimized_points.append(points[i]) + else: + vector1 = np.array(points[i]) - np.array(points[i - 1]) + vector2 = np.array(points[(i + 1) % len(points)]) - np.array(points[i]) + length1 = np.linalg.norm(vector1) + length2 = np.linalg.norm(vector2) + if length1 > 0 and length2 > 0: + dot = np.dot(vector1, vector2) / (length1 * length2) + # clamp dot between -1 and 1 + dot = max(-1, min(1, dot)) + angle = np.arccos(dot) + if angle > np.deg2rad(5): + optimized_points.append(points[i]) + print("Optimised from {} to {} points".format(len(points), len(optimized_points))) + return optimized_points + + +def chaikin(points, iterations): + if iterations == 0: + return points + l = len(points) + smoothed = [] + for i in range(l - 1): + x1, y1 = points[i] + x2, y2 = points[i + 1] + smoothed.append([0.95 * x1 + 0.05 * x2, 0.95 * y1 + 0.05 * y2]) + smoothed.append([0.05 * x1 + 0.95 * x2, 0.05 * y1 + 0.95 * y2]) + smoothed.append(points[l - 1]) + return chaikin(smoothed, iterations - 1) diff --git a/pcb_json.py b/pcb_json.py new file mode 100644 index 0000000..aa2a02c --- /dev/null +++ b/pcb_json.py @@ -0,0 +1,140 @@ +import json +import pandas as pd +import matplotlib.pyplot as plt + + +def create_track(points): + return [{"x": x, "y": y} for x, y in points] + + +def dump_json( + filename, + stator_radius, + stator_hole_radius, + track_width, + pad_diam, + pad_drill, + via_diam, + via_drill, + vias, + pads, + silk, + tracks_f, + tracks_b, +): + # dump out the results to json + json_result = { + "parameters": { + "trackWidth": track_width, + "statorHoleRadius": stator_hole_radius, + "statorRadius": stator_radius, + "viaDiameter": via_diam, + "viaDrillDiameter": via_drill, + "padDiameter": pad_diam, + "padDrillDiameter": pad_drill, + }, + "vias": vias, + "pads": pads, + "silk": silk, + "tracks": { + "f": [create_track(points) for points in tracks_f], + "b": [create_track(points) for points in tracks_b], + }, + } + json.dump(json_result, open(filename, "w")) + return json_result + + +def plot_json(json_result): + stator_radius = json_result["parameters"]["statorRadius"] + stator_hole_radius = json_result["parameters"]["statorHoleRadius"] + pad_diam = json_result["parameters"]["padDiameter"] + pad_drill = json_result["parameters"]["padDrillDiameter"] + # track_width = json_result["parameters"]["trackWidth"] + via_dim = json_result["parameters"]["viaDiameter"] + via_drill = json_result["parameters"]["viaDrillDiameter"] + # plot the back tracks + ax = None + for track in json_result["tracks"]["b"]: + df = pd.DataFrame(track, columns=["x", "y"]) + ax = df.plot.line(x="x", y="y", color="blue", ax=ax) + ax.axis("equal") + + # plot the front tracks + for track in json_result["tracks"]["f"]: + df = pd.DataFrame(track, columns=["x", "y"]) + ax = df.plot.line(x="x", y="y", color="red", ax=ax) + ax.axis("equal") + + # hide the legend + ax.legend().set_visible(False) + # make the plot bigger + ax.figure.set_size_inches(10, 10) + + # plot the vias + for via in json_result["vias"]: + ax.add_patch( + plt.Circle( + (via["x"], via["y"]), + radius=via_dim / 2, + fill=True, + color="black", + ) + ) + ax.add_patch( + plt.Circle( + (via["x"], via["y"]), + radius=via_drill / 2, + fill=True, + color="white", + ) + ) + + # plot the edge cuts + ax.add_patch( + plt.Circle( + (0, 0), + radius=stator_radius, + fill=False, + color="yellow", + ) + ) + ax.add_patch( + plt.Circle( + (0, 0), + radius=stator_hole_radius, + fill=False, + color="yellow", + ) + ) + + # plot the pads + for pad in json_result["pads"]: + ax.add_patch( + plt.Circle( + (pad["x"], pad["y"]), + radius=pad_diam / 2, + fill=True, + color="yellow", + ) + ) + ax.add_patch( + plt.Circle( + (pad["x"], pad["y"]), + radius=pad_drill / 2, + fill=True, + color="white", + ) + ) + + # plot the silk + for silk in json_result["silk"]: + ax.text( + silk["x"], + silk["y"], + silk["text"], + horizontalalignment="center", + verticalalignment="center", + color="black", + fontsize=50, + ) diff --git a/requirements.txt b/requirements.txt index b43a607..a0de6da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ jupyter numpy pandas scikit-spatial -scipy \ No newline at end of file +pre-commit \ No newline at end of file