Refactor common functions out from notebooks

This commit is contained in:
Chris Greening 2022-10-11 11:14:28 +01:00
parent 95abe5c798
commit f11f403e9f
6 changed files with 407 additions and 561 deletions

View file

@ -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 = []\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",
"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",
")\n",
"points_b = chaikin(\n",
" optimize_points(get_points(template, TURNS, TRACK_SPACING + TRACK_WIDTH)), 2\n",
" )\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)"
]
}
],

View file

@ -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)"
]
}
],

View file

@ -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)

112
helpers.py Normal file
View file

@ -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)

140
pcb_json.py Normal file
View file

@ -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,
)

View file

@ -3,4 +3,4 @@ jupyter
numpy
pandas
scikit-spatial
scipy
pre-commit