summaryrefslogtreecommitdiff
path: root/coil_test_board.py
diff options
context:
space:
mode:
authorjaseg <git@jaseg.de>2023-10-26 00:48:52 +0200
committerjaseg <git@jaseg.de>2023-10-26 00:48:52 +0200
commit9624e46147755d221c8e7cf519e9ecd416381857 (patch)
tree50d903f8d62eb17bfb3e41ebc7c236351b639bbd /coil_test_board.py
parenta35125b123bb0c645f6e06c97287e2fb6ef2d6cb (diff)
downloadgerbonara-9624e46147755d221c8e7cf519e9ecd416381857.tar.gz
gerbonara-9624e46147755d221c8e7cf519e9ecd416381857.tar.bz2
gerbonara-9624e46147755d221c8e7cf519e9ecd416381857.zip
Move coil stuff to separate repo
Diffstat (limited to 'coil_test_board.py')
-rw-r--r--coil_test_board.py483
1 files changed, 0 insertions, 483 deletions
diff --git a/coil_test_board.py b/coil_test_board.py
deleted file mode 100644
index af47ddf..0000000
--- a/coil_test_board.py
+++ /dev/null
@@ -1,483 +0,0 @@
-#!/usr/bin/env python3
-
-import math
-import hashlib
-import re
-import itertools
-import datetime
-import tempfile
-import subprocess
-import sqlite3
-import json
-from pathlib import Path
-
-import tqdm
-
-import gerbonara.cad.kicad.pcb as pcb
-import gerbonara.cad.kicad.footprints as fp
-import gerbonara.cad.primitives as cad_pr
-import gerbonara.cad.kicad.graphical_primitives as kc_gr
-
-
-cols = 5
-rows = 5
-
-coil_specs = [
- {'n': 1, 's': True, 't': 1, 'c': 0.20, 'w': 5.00, 'd': 3.00, 'v': 5.00},
- {'n': 2, 's': True, 't': 1, 'c': 0.20, 'w': 3.00, 'd': 1.50, 'v': 3.00},
- {'n': 3, 's': True, 't': 1, 'c': 0.20, 'w': 1.50, 'd': 1.20, 'v': 2.00},
- {'n': 5, 's': True, 't': 1, 'c': 0.20, 'w': 0.80, 'd': 0.40, 'v': 0.80},
- {'n': 10, 's': True, 't': 1, 'c': 0.20, 'w': 0.50, 'd': 0.30, 'v': 0.60},
- {'n': 25, 's': True, 't': 1, 'c': 0.15, 'w': 0.25, 'd': 0.30, 'v': 0.60},
-
- {'n': 1, 's': False, 't': 3, 'c': 0.20, 'w': 5.00, 'd': 3.00, 'v': 5.00},
- {'n': 2, 's': False, 't': 1, 'c': 0.20, 'w': 3.00, 'd': 1.50, 'v': 3.00},
- {'n': 3, 's': False, 't': 1, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 2.00},
- {'n': 5, 's': False, 't': 1, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 0.80},
- {'n': 10, 's': False, 't': 1, 'c': 0.20, 'w': 1.50, 'd': 0.80, 'v': 0.60},
- {'n': 25, 's': False, 't': 1, 'c': 0.15, 'w': 0.50, 'd': 0.30, 'v': 0.60},
-
- {'n': 1, 's': False, 't': 4, 'c': 0.20, 'w': 5.00, 'd': 3.00, 'v': 5.00},
- {'n': 2, 's': False, 't': 3, 'c': 0.20, 'w': 3.00, 'd': 1.50, 'v': 3.00},
- {'n': 3, 's': False, 't': 4, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 2.00},
- {'n': 5, 's': False, 't': 3, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 0.80},
- {'n': 10, 's': False, 't': 3, 'c': 0.20, 'w': 1.50, 'd': 0.80, 'v': 0.60},
- {'n': 25, 's': False, 't': 3, 'c': 0.15, 'w': 0.50, 'd': 0.30, 'v': 0.60},
-
- {'n': 1, 's': False, 't': 5, 'c': 0.20, 'w': 5.00, 'd': 3.00, 'v': 5.00},
- {'n': 2, 's': False, 't': 5, 'c': 0.20, 'w': 3.00, 'd': 1.50, 'v': 3.00},
- {'n': 3, 's': False, 't': 4, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 2.00},
- {'n': 5, 's': False, 't': 7, 'c': 0.20, 'w': 2.50, 'd': 1.20, 'v': 0.80},
- {'n': 10, 's': False, 't': 7, 'c': 0.20, 'w': 1.50, 'd': 0.80, 'v': 0.60},
- {'n': 25, 's': False, 't': 13, 'c': 0.15, 'w': 0.50, 'd': 0.30, 'v': 0.60},
-
- {'n': 25, 's': False, 't': 37, 'c': 0.15, 'w': 0.50, 'd': 0.30, 'v': 0.60},
-]
-
-cachedir = Path('/tmp/coil_test_cache')
-version_string = 'v1.0'
-coil_border = 7 # mm
-cut_gap = 8 # mm
-tooling_border = 10 # mm
-vscore_extra = 10 # mm
-mouse_bite_width = 8 # mm
-mouse_bite_yoff = 0.175
-mouse_bite_hole_dia = 0.7
-mouse_bite_hole_spacing = 0.7
-hole_offset = 5
-hole_dia = 3.2
-coil_dia = 35 # mm
-coil_inner_dia = 15 # mm
-board_thickness = 0.80 # mm
-pad_offset = 2 # mm
-pad_dia = 2.0 # mm
-pad_length = 3.5 # mm
-pad_drill = 1.1 # mm
-pad_pitch = 2.54 # mm
-join_trace_w = 0.150 # mm
-do_v_cuts = False
-do_mouse_bites = False
-do_cut_gaps = False
-
-db = sqlite3.connect('coil_parameters.sqlite3')
-db.execute('CREATE TABLE IF NOT EXISTS runs (run_id INTEGER PRIMARY KEY, timestamp TEXT, version TEXT)')
-db.execute('CREATE TABLE IF NOT EXISTS coils (coil_id INTEGER PRIMARY KEY, run_id INTEGER, FOREIGN KEY (run_id) REFERENCES runs(run_id))')
-db.execute('CREATE TABLE IF NOT EXISTS results (result_id INTEGER PRIMARY KEY, coil_id INTEGER, key TEXT, value TEXT, FOREIGN KEY (coil_id) REFERENCES coils(coil_id))')
-cur = db.cursor()
-cur.execute('INSERT INTO runs(timestamp, version) VALUES (datetime("now"), ?)', (version_string,))
-run_id = cur.lastrowid
-db.commit()
-
-tile_width = tile_height = coil_dia + 2*coil_border
-coil_pitch_v = tile_width + cut_gap
-coil_pitch_h = tile_height + cut_gap
-
-total_width = coil_pitch_h*cols + 2*tooling_border + cut_gap
-total_height = coil_pitch_v*rows + 2*tooling_border + cut_gap
-
-drawing_text_size = 2.0
-
-print(f'Calculated board size: {total_width:.2f} * {total_height:.2f} mm')
-print(f'Tile size: {tile_height:.2f} * {tile_height:.2f} mm')
-
-x0, y0 = 100, 100
-
-xy = pcb.XYCoord
-b = pcb.Board.empty_board(page=pcb.PageSettings(page_format='A2'))
-
-b.add(kc_gr.Rectangle(xy(x0, y0), xy(x0+total_width, y0+total_height), layer='Edge.Cuts', stroke=pcb.Stroke(width=0.15)))
-
-def do_line(x0, y0, x1, y1, off_x=0, off_y=0):
- b.add(kc_gr.Line(xy(x0+off_x, y0+off_y),
- xy(x1+off_x, y1+off_y),
- layer='Edge.Cuts', stroke=pcb.Stroke(width=0.15)))
-
-if do_v_cuts:
- for y in range(rows):
- for off_y in [0, tile_height]:
- y_pos = y0 + tooling_border + cut_gap + off_y + y*coil_pitch_v
- do_line(x0 - vscore_extra, y_pos, x0 + total_width + vscore_extra, y_pos)
- b.add(kc_gr.Text(text='V-score',
- at=pcb.AtPos(x0 + total_width + vscore_extra + drawing_text_size/2, y_pos, 0),
- layer=kc_gr.TextLayer('Edge.Cuts'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(size=xy(drawing_text_size, drawing_text_size),
- thickness=drawing_text_size/10),
- justify=pcb.Justify(h=pcb.Atom.left))))
-
-
- for x in range(cols):
- for off_x in [0, tile_width]:
- x_pos = x0 + tooling_border + cut_gap + off_x + x*coil_pitch_h
- do_line(x_pos, y0 - vscore_extra, x_pos, y0 + total_height + vscore_extra)
- b.add(kc_gr.Text(text='V-score',
- at=pcb.AtPos(x_pos, y0 + total_height + vscore_extra + drawing_text_size/2, 90),
- layer=kc_gr.TextLayer('Edge.Cuts'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(size=xy(drawing_text_size, drawing_text_size),
- thickness=drawing_text_size/10),
- justify=pcb.Justify(h=pcb.Atom.right))))
-
-def draw_corner(x0, y0, spokes):
- right, top, left, bottom = [True if c.lower() in 'y1' else False for c in spokes]
-
- l = (tile_width - mouse_bite_width)/2 - cut_gap/2
-
- if right:
- do_line(cut_gap/2, -cut_gap/2, cut_gap/2 + l, -cut_gap/2, x0, y0)
- do_line(cut_gap/2, cut_gap/2, cut_gap/2 + l, cut_gap/2, x0, y0)
- b.add(kc_gr.Arc(start=xy(x0+cut_gap/2+l, y0-cut_gap/2),
- end=xy(x0+cut_gap/2+l, y0+cut_gap/2),
- center=xy(x0+cut_gap/2+l, y0),
- layer='Edge.Cuts',
- stroke=pcb.Stroke(width=0.15)))
-
- else:
- do_line(cut_gap/2, -cut_gap/2, cut_gap/2, cut_gap/2, x0, y0)
-
- if left:
- do_line(-cut_gap/2, -cut_gap/2, -cut_gap/2 - l, -cut_gap/2, x0, y0)
- do_line(-cut_gap/2, cut_gap/2, -cut_gap/2 - l, cut_gap/2, x0, y0)
- b.add(kc_gr.Arc(end=xy(x0-cut_gap/2-l, y0-cut_gap/2),
- start=xy(x0-cut_gap/2-l, y0+cut_gap/2),
- center=xy(x0-cut_gap/2-l, y0),
- layer='Edge.Cuts',
- stroke=pcb.Stroke(width=0.15)))
-
- else:
- do_line(-cut_gap/2, -cut_gap/2, -cut_gap/2, cut_gap/2, x0, y0)
-
- if bottom:
- do_line(-cut_gap/2, cut_gap/2, -cut_gap/2, cut_gap/2 + l, x0, y0)
- do_line(cut_gap/2, cut_gap/2, cut_gap/2, cut_gap/2 + l, x0, y0)
- b.add(kc_gr.Arc(end=xy(x0-cut_gap/2, y0+cut_gap/2+l),
- start=xy(x0+cut_gap/2, y0+cut_gap/2+l),
- center=xy(x0, y0+cut_gap/2+l),
- layer='Edge.Cuts',
- stroke=pcb.Stroke(width=0.15)))
-
- else:
- do_line(-cut_gap/2, cut_gap/2, cut_gap/2, cut_gap/2, x0, y0)
-
- if top:
- do_line(-cut_gap/2, -cut_gap/2, -cut_gap/2, -cut_gap/2 - l, x0, y0)
- do_line(cut_gap/2, -cut_gap/2, cut_gap/2, -cut_gap/2 - l, x0, y0)
- b.add(kc_gr.Arc(start=xy(x0-cut_gap/2, y0-cut_gap/2-l),
- end=xy(x0+cut_gap/2, y0-cut_gap/2-l),
- center=xy(x0, y0-cut_gap/2-l),
- layer='Edge.Cuts',
- stroke=pcb.Stroke(width=0.15)))
- else:
-
- do_line(-cut_gap/2, -cut_gap/2, cut_gap/2, -cut_gap/2, x0, y0)
-
-
-def make_mouse_bite(x, y, rot=0, width=mouse_bite_width, hole_dia=mouse_bite_hole_dia, hole_spacing=mouse_bite_hole_spacing, **kwargs):
-
- pitch = hole_dia + hole_spacing
- num_holes = int(math.floor((width - hole_dia) / pitch)) + 1
-
- actual_spacing = (width - num_holes*hole_dia) / (num_holes - 1)
- pitch = hole_dia + actual_spacing
-
- f = fp.Footprint(name='mouse_bite', _version=None, generator=None, at=fp.AtPos(x, y, rot), **kwargs)
- for i in range(num_holes):
- f.pads.append(fp.Pad(
- number='1',
- type=fp.Atom.np_thru_hole,
- shape=fp.Atom.circle,
- at=fp.AtPos(-width/2 + i*pitch + hole_dia/2, 0, 0),
- size=xy(hole_dia, hole_dia),
- drill=fp.Drill(diameter=hole_dia),
- footprint=f))
- return f
-
-
-def make_hole(x, y, dia, **kwargs):
- f = fp.Footprint(name='hole', _version=None, generator=None, at=fp.AtPos(x, y, 0), **kwargs)
- f.pads.append(fp.Pad(
- number='1',
- type=fp.Atom.np_thru_hole,
- shape=fp.Atom.circle,
- at=fp.AtPos(0, 0, 0),
- size=xy(dia, dia),
- drill=fp.Drill(diameter=dia),
- footprint=f))
- return f
-
-
-def make_pads(x, y, rot, n, pad_dia, pad_length, drill, pitch, **kwargs):
- f = fp.Footprint(name=f'conn_gen_01x{n}', _version=None, generator=None, at=fp.AtPos(x, y, rot), **kwargs)
-
- for i in range(n):
- f.pads.append(fp.Pad(
- number=str(i+1),
- type=fp.Atom.thru_hole,
- shape=fp.Atom.oval,
- at=fp.AtPos(-pitch*(n-1)/2 + i*pitch, 0, rot),
- size=xy(pad_dia, pad_length),
- drill=fp.Drill(diameter=drill),
- footprint=f))
-
- return f
-
-
-corner_x0 = x0 + tooling_border + cut_gap/2
-corner_y0 = y0 + tooling_border + cut_gap/2
-corner_x1 = x0 + total_width - tooling_border - cut_gap/2
-corner_y1 = y0 + total_height - tooling_border - cut_gap/2
-
-if do_cut_gaps:
- # Corners
- draw_corner(corner_x0, corner_y0, 'YNNY')
- draw_corner(corner_x0, corner_y1, 'YYNN')
- draw_corner(corner_x1, corner_y0, 'NNYY')
- draw_corner(corner_x1, corner_y1, 'NYYN')
-
- # Top / bottom T junctions
- for x in range(1, cols):
- draw_corner(corner_x0 + x*coil_pitch_h, corner_y0, 'YYNY')
- draw_corner(corner_x0 + x*coil_pitch_h, corner_y1, 'NYYY')
-
- # Left / right T junctions
- for y in range(1, rows):
- draw_corner(corner_x0, corner_y0 + y*coil_pitch_v, 'YYNY')
- draw_corner(corner_x1, corner_y0 + y*coil_pitch_v, 'NYYY')
-
- # Middle X junctions
- for y in range(1, rows):
- for x in range(1, cols):
- draw_corner(corner_x0 + x*coil_pitch_h, corner_y0 + y*coil_pitch_v, 'YYYY')
-
-else:
- for layer in ('F.SilkS', 'B.SilkS'):
- for x in range(0, cols+1):
- cx = x0 + tooling_border + cut_gap/2 + x*coil_pitch_h
- b.add(kc_gr.Line(xy(cx, corner_y0),
- xy(cx, corner_y1),
- layer=layer, stroke=pcb.Stroke(width=0.15)))
-
- for y in range(0, rows+1):
- cy = y0 + tooling_border + cut_gap/2 + y*coil_pitch_v
- b.add(kc_gr.Line(xy(corner_x0, cy),
- xy(corner_x1, cy),
- layer=layer, stroke=pcb.Stroke(width=0.15)))
-
-
-# Mouse bites
-if do_mouse_bites:
- for x in range(0, cols):
- for y in range(0, rows):
- tile_x0 = x0 + tooling_border + cut_gap + x*coil_pitch_h
- tile_y0 = y0 + tooling_border + cut_gap + y*coil_pitch_v
-
- b.add(make_mouse_bite(tile_x0 + tile_width/2, tile_y0 - mouse_bite_hole_dia/2, 0))
- b.add(make_mouse_bite(tile_x0 + tile_width/2, tile_y0 + tile_height + mouse_bite_hole_dia/2, 0))
- b.add(make_mouse_bite(tile_x0 - mouse_bite_hole_dia/2, tile_y0 + tile_height/2, 90))
- b.add(make_mouse_bite(tile_x0 + tile_width + mouse_bite_hole_dia/2, tile_y0 + tile_height/2, 90))
-
-# Mounting holes
-for x in range(0, cols):
- for y in range(0, rows):
- tile_x0 = x0 + tooling_border + cut_gap + x*coil_pitch_h + tile_width/2
- tile_y0 = y0 + tooling_border + cut_gap + y*coil_pitch_v + tile_height/2
-
- dx = tile_width/2 - hole_offset
- dy = tile_height/2 - hole_offset
- b.add(make_hole(tile_x0 - dx, tile_y0 - dy, hole_dia))
- b.add(make_hole(tile_x0 - dx, tile_y0 + dy, hole_dia))
- b.add(make_hole(tile_x0 + dx, tile_y0 - dy, hole_dia))
- b.add(make_hole(tile_x0 + dx, tile_y0 + dy, hole_dia))
-
-# border graphics
-c = 3
-for layer in ['F.SilkS', 'B.SilkS']:
- b.add(kc_gr.Rectangle(start=xy(x0, y0), end=xy(x0+c, y0+total_height), layer=layer, stroke=pcb.Stroke(width=0),
- fill=kc_gr.FillMode(pcb.Atom.solid)))
- b.add(kc_gr.Rectangle(start=xy(x0, y0), end=xy(x0+total_width, y0+c), layer=layer, stroke=pcb.Stroke(width=0),
- fill=kc_gr.FillMode(pcb.Atom.solid)))
- b.add(kc_gr.Rectangle(start=xy(x0+total_width-c, y0), end=xy(x0+total_width, y0+total_height), layer=layer, stroke=pcb.Stroke(width=0),
- fill=kc_gr.FillMode(pcb.Atom.solid)))
- b.add(kc_gr.Rectangle(start=xy(x0, y0+total_height-c), end=xy(x0+total_width, y0+total_height), layer=layer, stroke=pcb.Stroke(width=0),
- fill=kc_gr.FillMode(pcb.Atom.solid)))
-
-a = 3
-timestamp = datetime.datetime.now().strftime('%Y-%m-%d')
-b.add(kc_gr.Text(text=f'Planar inductor test panel',
- at=pcb.AtPos(x0 + tooling_border + cut_gap/2, y0 + c + 2*a/3),
- layer=kc_gr.TextLayer('F.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face="Inter Semi Bold",
- size=xy(6*a/3, 6*a/3),
- thickness=a/5),
- justify=pcb.Justify(h=pcb.Atom.left, v=pcb.Atom.top))))
-
-b.add(kc_gr.Text(text=f'{version_string} {timestamp} © 2023 Jan Götte, FG KOM, TU Darmstadt',
- at=pcb.AtPos(x0 + total_width - tooling_border - cut_gap/2, y0 + c + 4*a/3),
- layer=kc_gr.TextLayer('F.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face="Inter Light",
- size=xy(a, a),
- thickness=a/5),
- justify=pcb.Justify(h=pcb.Atom.right, v=pcb.Atom.top))))
-
-for index, ((y, x), spec) in tqdm.tqdm(enumerate(zip(itertools.product(range(rows), range(cols)), coil_specs), start=1)):
- pass
- with tempfile.NamedTemporaryFile(suffix='.kicad_mod') as f:
- tile_x0 = x0 + tooling_border + cut_gap + x*coil_pitch_h + tile_width/2
- tile_y0 = y0 + tooling_border + cut_gap + y*coil_pitch_v + tile_height/2
-
- for key, alias in {
- 'gen.inner_diameter': 'id',
- 'gen.outer_diameter': 'od',
- 'gen.trace_width': 'w',
- 'gen.turns': 'n',
- 'gen.twists': 't',
- 'gen.clearance': 'c',
- 'gen.single_layer': 's',
- 'gen.via_drill': 'd',
- 'gen.via_diameter': 'v'}.items():
- if alias in spec:
- spec[key] = spec.pop(alias)
-
- if 'gen.via_diameter' not in spec:
- spec['gen.via_diameter'] = spec['gen.trace_width']
-
- if 'gen.inner_diameter' not in spec:
- spec['gen.inner_diameter'] = coil_inner_dia
-
- if 'gen.outer_diameter' not in spec:
- spec['gen.outer_diameter'] = coil_dia
-
- args = ['python', '-m', 'twisted_coil_gen_twolayer', '--no-keepout-zone']
- for k, v in spec.items():
- prefix, _, k = k.partition('.')
- if (not isinstance(v, bool) or v) and prefix == 'gen':
- args.append('--' + k.replace('_', '-'))
- if v is not True:
- args.append(str(v))
-
- arg_digest = hashlib.sha3_256(' / '.join(map(str, args)).encode()).hexdigest()
- cachedir.mkdir(exist_ok=True)
- cache_file = cachedir / f'C-{arg_digest}.kicad_mod'
- log_file = cachedir / f'Q-{arg_digest}.kicad_mod'
- if not cache_file.is_file():
- args.append(cache_file)
- try:
- res = subprocess.run(args, check=True, capture_output=True, text=True)
- log_file.write_text(res.stdout + res.stderr)
- except subprocess.CalledProcessError as e:
- print(f'Error generating coil with command line {args}, rc={e.returncode}')
- print(e.stdout)
- print(e.stderr)
-
- coil = fp.Footprint.open_mod(cache_file)
- coil.at = fp.AtPos(tile_x0, tile_y0, 0)
- b.add(coil)
-
- t = [f'n={spec["gen.turns"]}',
- f'{spec["gen.twists"]} twists',
- f'w={spec["gen.trace_width"]:.2f}mm']
- if spec.get('gen.single_layer'):
- t.append('single layer')
-
- spec['gen.board_thickness'] = board_thickness
- cur.execute('INSERT INTO coils(run_id) VALUES (?)', (run_id,))
- coil_id = cur.lastrowid
-
- for key, value in spec.items():
- if isinstance(value, bool):
- value = str(value)
- db.execute('INSERT INTO results(coil_id, key, value) VALUES (?, ?, ?)', (coil_id, key, value))
-
- for l in log_file.read_text().splitlines():
- if (m := re.fullmatch(r'Approximate inductance:\s*([-+.0-9eE]+)\s*µH', l.strip())):
- val = float(m.group(1)) * 1e-6
- db.execute('INSERT INTO results(coil_id, key, value) VALUES (?, "calculated_approximate_inductance", ?)', (coil_id, val))
- if (m := re.fullmatch(r'Approximate track length:\s*([-+.0-9eE]+)\s*mm', l.strip())):
- val = float(m.group(1)) * 1e-3
- db.execute('INSERT INTO results(coil_id, key, value) VALUES (?, "calculated_trace_length", ?)', (coil_id, val))
- if (m := re.fullmatch(r'Approximate resistance:\s*([-+.0-9eE]+)\s*Ω', l.strip())):
- val = float(m.group(1))
- db.execute('INSERT INTO results(coil_id, key, value) VALUES (?, "calculated_approximate_resistance", ?)', (coil_id, val))
- if (m := re.fullmatch(r'Fill factor:\s*([-+.0-9eE]+)', l.strip())):
- val = float(m.group(1))
- db.execute('INSERT INTO results(coil_id, key, value) VALUES (?, "calculated_fill_factor", ?)', (coil_id, val))
- db.commit()
-
- sz = 2
- b.add(kc_gr.Text(text='\\n'.join(t),
- at=pcb.AtPos(tile_x0, tile_y0),
- layer=kc_gr.TextLayer('B.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face='Inter Medium',
- size=xy(sz, sz),
- thickness=sz/5),
- justify=pcb.Justify(h=None, v=None, mirror=True))))
-
- b.add(kc_gr.Text(text=f'Tile {index}',
- at=pcb.AtPos(tile_x0, tile_y0 - tile_height/2 + sz),
- layer=kc_gr.TextLayer('B.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face='Inter Semi Bold',
- size=xy(sz, sz),
- thickness=sz/5),
- justify=pcb.Justify(h=None, v=pcb.Atom.top, mirror=True))))
-
- b.add(kc_gr.Text(text=f'{version_string} {timestamp}',
- at=pcb.AtPos(tile_x0, tile_y0 - tile_height/2 + sz*2.4),
- layer=kc_gr.TextLayer('B.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face='Inter Light',
- size=xy(sz, sz),
- thickness=sz/5),
- justify=pcb.Justify(h=None, v=pcb.Atom.top, mirror=True))))
-
- b.add(kc_gr.Text(text=f'{index}',
- at=pcb.AtPos(tile_x0, tile_y0 - tile_height/2 + sz),
- layer=kc_gr.TextLayer('F.SilkS'),
- effects=pcb.TextEffect(
- font=pcb.FontSpec(face='Inter Medium',
- size=xy(sz, sz),
- thickness=sz/5),
- justify=pcb.Justify(h=None, v=pcb.Atom.top, mirror=False))))
-
- pads_x0 = tile_x0 + tile_width/2 - pad_offset
- pads = make_pads(pads_x0, tile_y0, 270, 2, pad_dia, pad_length, pad_drill, pad_pitch)
- b.add(pads)
-
- w = min(spec.get('gen.trace_width', pad_dia), pad_dia)
- wx, wy, _r, _f = pads.pad(2).abs_pos
- w2 = (wx - pad_length/2, wy)
- wx, wy, _r, _f = pads.pad(1).abs_pos
- w1 = (wx - pad_length/2, wy)
- b.add(cad_pr.Trace(w, coil.pad(1), pads.pad(1), waypoints=[w1], orientation=['ccw'], side='top'))
- b.add(cad_pr.Trace(w, coil.pad(2), pads.pad(2), waypoints=[w2], orientation=['cw'], side='bottom'))
-
- k = 3
- for layer in ['F.SilkS', 'B.SilkS']:
- b.add(kc_gr.Rectangle(start=xy(wx-k/2, wy-pad_pitch-k/2), end=xy(wx+k/2, wy-pad_pitch), layer=layer, stroke=pcb.Stroke(width=0),
- fill=kc_gr.FillMode(pcb.Atom.solid)))
-
-b.write('coil_test_board.kicad_pcb')
-