def exportKiCadModule( self, filename, footprint_name='EM-Structure', description="EM Structure imported from Gerber file format.", tags="em structure gerber"): mod = kmt.Footprint(footprint_name) mod.setDescription(description) mod.setTags(tags) # set general values mod.append( kmt.Text(type='reference', text='REF**', at=[0, -3], layer='F.SilkS')) mod.append( kmt.Text(type='value', text=footprint_name, at=[1.5, 3], layer='F.Fab')) # create silscreen #mod.append(kmt.RectLine(start=[-2, -2], end=[5, 2], layer='F.SilkS')) (minx, miny, maxx, maxy) = self.boundingBox() w = maxx - minx h = maxy - miny ox = -maxx + w / 2 oy = -maxy + h / 2 # create courtyard mod.append( kmt.RectLine(start=[-w / 2, -h / 2], end=[w / 2, h / 2], layer='F.CrtYd')) #mod.append(kmt.FilledRect(start=[-w/2, -h/2], end=[w/2, h/2], layer='F.Mask')) n = 1 # iterate layers for layer in self.gerberLayers: n = layer.appendKicadLayer(mod, mod_layer=layer.getID(), offset_x=ox, offset_y=oy, startpad=n) # output kicad model file_handler = kmt.KicadFileHandler(mod) file_handler.writeFile(filename)
def coil_footrpint(name, description, tags, other_parts): fp = kmt.Footprint(name) fp.setDescription(description) fp.setTags(tags) # add some text: reference and value fp.append( kmt.Text(type='reference', text='REF**', at=[0, -2], layer='F.SilkS')) fp.append(kmt.Text(type='value', text=name, at=[1.5, 2], layer='F.Fab')) for part in other_parts: fp.append(part) return fp
def coil_pads(start_point, end_point, line_width, drill, pad_type, pad_shape): if pad_type == 'SMT': pad_type = kmt.Pad.TYPE_THT pad_layer = kmt.Pad.LAYERS_THT drill_kw = dict(drill=drill) elif pad_type == 'THT': pad_type = kmt.Pad.TYPE_THT pad_layer = kmt.Pad.LAYERS_THT drill_kw = dict(drill=drill) elif pad_type == 'CONNECT': pad_type = kmt.Pad.TYPE_CONNECT pad_layer = kmt.Pad.LAYERS_THT drill_kw = dict() else: raise Exception('pad_type must be one of "SMT","THT" or "CONNECT"') if pad_shape == 'RECTANGLE': pad_shape = kmt.Pad.SHAPE_RECT elif pad_shape == 'CIRCLE': pad_shape = kmt.Pad.SHAPE_CIRCLE else: raise Exception('pad_type must be one of "RECTANGLE" or "CIRCLE"') if line_width - drill < 0.25: raise Exception('The width of the ring has to be greater than 0.25mm') pads = [] for i, pos in enumerate([start_point, end_point]): pad = kmt.Pad(number=i + 1, type=pad_type, shape=pad_shape, at=pos, size=[line_width, line_width], layers=pad_layer, **drill_kw) pads.append(pad) return pads
def _boundToKmtPoly(self, bound, layer, offset_x, offset_y): # get coordinates in (x, y) form m = geo.mapping(bound) c = m['coordinates'] # translate coordinates with offset c_transl = [] for x, y in c: c_transl.append([x + offset_x, -y - offset_y]) return kmt.Polygon(nodes=c_transl, layer=layer, width=0)
def coil_lines(params, direction=+1): """ Creates a square spiral coil. It is assumed that we want to fill as much space as possible, and so the length of line is shortened only after full turn. The coil starts at corner of the square and ends in "previous" corner. """ n_turns = int(params.n_turns) if abs(n_turns - params.n_turns) > 0.01: print('[WARNING] square coil can only have integer number of turns;' ' reducing n_turns to %d' % n_turns) points = [] points.append((params.r_outer, -params.r_outer)) for i in range(n_turns): r = params.r_outer - i * delta_r(params) next_r = r - delta_r(params) if direction > 0: turn_points = [ (-r, -r), (-r, r), (r, r), (r, -next_r), ] else: turn_points = [ (r, r), (-r, r), (-r, -r), (next_r, -r), ] points.extend(turn_points) lines = [] for i in range(len(points) - 1): lines.append( kmt.Line(start=points[i], end=points[i + 1], width=params.line_width, layer='F.Cu')) start_point = points[0] end_point = points[-1] return lines, start_point, end_point
def coil_pads(start_point, end_point, line_width, drill): pad_type = kmt.Pad.TYPE_THT pad_layer = kmt.Pad.LAYERS_THT drill_kw = dict(drill=drill) pad_shape = kmt.Pad.SHAPE_CIRCLE pads = [] for i, pos in enumerate([start_point, end_point]): pad = kmt.Pad(number=i + 1, type=pad_type, shape=pad_shape, at=pos, size=[line_width, line_width], layers=pad_layer, **drill_kw) pads.append(pad) return pads
def appendKicadLayer(self, kicad_mod, mod_layer='F.Cu', offset_x=0, offset_y=0, startpad=1): ''' Write the layer to a kicad_mod object from the KicadModTree. ''' n = startpad # iterate closed polygons for net in self.nets: #if len(net.getPads()) == 0: # no pad = poly primitive # get polygon object poly = net.getPolygon() # convert to KMT polygon kmt_poly = self._polyToKmtPoly(poly, mod_layer, offset_x, offset_y) # save kicad_mod.append(kmt_poly) #else: pads = net.getPads() # # first pad is anchor # anch = pads[0] # # # center point # (pxmin, pymin, pxmax, pymax) = anch.bounds # px = (pxmin + pxmax) / 2 # py = (pymin + pymax) / 2 # # # size and rotation # x1, y1 = anch.boundary.coords[0] # x2, y2 = anch.boundary.coords[1] # x3, y3 = anch.boundary.coords[2] # w = sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2) # h = sqrt((x2 - x3) ** 2 + (y2 - y3) ** 2) # rot = atan2((y1 - y2), (x1 - x2)) / pi * 180 # # # set pad rotation and transform polygon accordingly # rot_poly = aff.rotate(net.getPolygon(), -rot, (px, py)) # # # convert to KMT polygon # kmt_poly = self._polyToKmtPoly(rot_poly, mod_layer, -px, -py) # # # create pad # kicad_mod.append(kmt.Pad(number = n, type=kmt.Pad.TYPE_SMT, shape = kmt.Pad.SHAPE_CUSTOM, layers = [mod_layer], # at=[px + offset_x, -(py + offset_y)], size=[w, h], rotation=rot, primitives=[kmt_poly], anchor_shape=kmt.Pad.SHAPE_RECT)) # # other pads are simple squares #for i in range(1, len(pads)): for i in range(len(pads)): # center point (pxmin, pymin, pxmax, pymax) = pads[i].bounds px = (pxmin + pxmax) / 2 + offset_x py = -((pymin + pymax) / 2 + offset_y) # size and rotation x1, y1 = pads[i].boundary.coords[0] x2, y2 = pads[i].boundary.coords[1] x3, y3 = pads[i].boundary.coords[2] w = sqrt((x1 - x2)**2 + (y1 - y2)**2) h = sqrt((x2 - x3)**2 + (y2 - y3)**2) rot = atan2((y1 - y2), (x1 - x2)) / pi * 180 kicad_mod.append( kmt.Pad(number=n, type=kmt.Pad.TYPE_SMT, shape=kmt.Pad.SHAPE_RECT, layers=[mod_layer], at=[px, py], size=[w, h], rotation=rot)) # increase pad number n = n + 1 return n
def save_footprint(footprint, file_name): if not file_name.endswith('.kicad_mod'): file_name = file_name + '.kicad_mod' file_handler = kmt.KicadFileHandler(footprint) file_handler.writeFile(file_name)
lines = [''] * 4 for name, color in colors.items(): parts = get_ball_desc(grid[m][n]) for i in range(1): cstr = '\033[38;2;' + color[0] + 'm' + '\033[48;2;' + color[1] + 'm' end = '\033[0m' print('██' + cstr + '{:<8}'.format(name) + end, end='') print('██') print('█' * (10 * len(colors.items()) + 2)) print() print('Device Name: ' + device_name) print('Package: ' + package) if kicadMod: mod = kmt.Footprint(package) mod.setDescription(package + ' package') border = 1 # 1 mm space around edges, approximately enough for every package mod.append( kmt.Text(type='reference', text='REF**', at=[(max_m + 1) / 2 * pitch, pitch - border - 3], layer='F.SilkS')) mod.append( kmt.Text(type='value', text=package, at=[(max_m + 1) / 2 * pitch, pitch - border - 1], layer='F.Fab'))
def rectangle_coil_lines(coil_params, board_layer, via_offset, winding_direction): width = coil_params[0] height = coil_params[1] line_width = coil_params[2] line_spacing = coil_params[3] n_turns = int(coil_params[4]) points = [] ##################### ## FRONT POSITIVE if (board_layer == 'F.Cu') and (winding_direction == 1): points.append((-width / 2, height / 2 + via_offset)) points.append((-width / 2, height / 2)) for i in np.arange(n_turns): x = width / 2 - i * (line_width + line_spacing) y = height / 2 - i * (line_width + line_spacing) next_y = y - (line_width + line_spacing) turn_points = [(x, y), (x, -y), (-x, -y), (-x, next_y)] points.extend(turn_points) #For the last point, add an offset for the Via points.append((0, next_y)) next_y = y - via_offset points.append((0, next_y)) ##################### ## BACK POSITIVE elif (board_layer == 'B.Cu') and (winding_direction == 1): points.append((width / 2, height / 2 + via_offset)) points.append((width / 2, height / 2)) for i in np.arange(n_turns): x = width / 2 - i * (line_width + line_spacing) y = height / 2 - i * (line_width + line_spacing) next_y = y - (line_width + line_spacing) # turn_points = [(x, -y),(x, -y),(-x, -y),(-x, next_y)] turn_points = [(-x, y), (-x, -y), (x, -y), (x, next_y)] points.extend(turn_points) #For the last point, add an offset for the Via points.append((0, next_y)) next_y = y - via_offset points.append((0, next_y)) ##################### ## FRONT NEGATIVE elif (board_layer == 'F.Cu') and (winding_direction == -1): points.append((-width / 2, height / 2 + via_offset)) points.append((-width / 2, height / 2)) for i in np.arange(n_turns): x = width / 2 - i * (line_width + line_spacing) y = height / 2 - i * (line_width + line_spacing) next_x = x - (line_width + line_spacing) turn_points = [(-x, -y), (x, -y), (x, y), (-next_x, y)] points.extend(turn_points) #For the last point, add an offset for the Via next_y = height / 2 - (n_turns) * ( line_width + line_spacing) #next_y not defined for direction == -1 next_y = y - via_offset points.append((-next_x, next_y)) next_x = next_x - via_offset points.append((-next_x, next_y)) ##################### ## BACK NEGATIVE elif (board_layer == 'B.Cu') and (winding_direction == -1): points.append((-width / 2, height / 2 + via_offset)) points.append((-width / 2, height / 2)) for i in np.arange(n_turns): x = width / 2 - i * (line_width + line_spacing) y = height / 2 - i * (line_width + line_spacing) next_x = x - (line_width + line_spacing) turn_points = [(-x, -y), (x, -y), (x, y), (-next_x, y)] points.extend(turn_points) #For the last point, add an offset for the Via next_y = height / 2 - (n_turns) * ( line_width + line_spacing) #next_y not defined for direction == -1 next_y = y - via_offset points.append((-next_x, next_y)) next_x = next_x - via_offset points.append((-next_x, next_y)) lines = [] for i in np.arange(len(points) - 1): lines.append( kmt.Line(start=points[i], end=points[i + 1], width=line_width, layer=board_layer)) start_point = points[0] end_point = points[-1] return lines, start_point, end_point
def coil_arcs(params, points_per_turn=4, direction=+1): """ Approximates Archimedean spiral by a sequence of arcs. For each spiral revolution 'points_per_turn' are taken and an arc for each pair is drawn. The coordinates of start and end points of the line are returned as (x, y). Archimedean spiral is defined in polar coordinates by equation: r = a + b*theta where 'a' is the inner radius. We can calculate the outer radius as: R = a + b*total_theta And solve for b: b = (R - a) / total_theta We first define the spiral as if it had 0 line width, only when generating footprint we draw with the actual width. """ total_angle = 360 * params.n_turns b = (params.r_outer - params.r_inner) / math.radians(total_angle) * direction # calculate all the angles at which we evaluate points angle_increment = 360.0 / points_per_turn n_increments = int(total_angle / angle_increment) angles = [angle_increment * i for i in range(n_increments)] if angles[-1] < total_angle: angles += [total_angle] def cartesian_coords_for_angle(angle_deg): theta = math.radians(angle_deg) r = params.r_inner + b * theta x, y = polar2cartesian(r, theta) return x, y arcs = [] for i in range(len(angles) - 1): start_angle = angles[i] end_angle = angles[i + 1] mid_angle = (start_angle + end_angle) / 2 start = cartesian_coords_for_angle(start_angle) mid = cartesian_coords_for_angle(mid_angle) end = cartesian_coords_for_angle(end_angle) # calculate the center and angle of the arc center, angle_rad = arc_through_3_points(start, mid, end) if direction > 0: if angle_rad < 0: angle_rad += 2 * pi else: if angle_rad > 0: angle_rad -= 2 * pi # just draw everything on front copper layer layer = 'F.Cu' arcs.append(kmt.Arc(center=center, start=start, angle=math.degrees(angle_rad), layer=layer, width=params.line_width)) start_point = cartesian_coords_for_angle(angles[0]) end_point = cartesian_coords_for_angle(angles[-1]) return arcs, start_point, end_point
def exportToKicadMod( self, filename, footprint_name='EM-Structure', description="EM Structure imported from Gerber file format.", tags="em structure gerber"): mod = kmt.Footprint(footprint_name) mod.setDescription(description) mod.setTags(tags) # set general values mod.append( kmt.Text(type='reference', text='REF**', at=[0, -3], layer='F.SilkS')) mod.append( kmt.Text(type='value', text=footprint_name, at=[1.5, 3], layer='F.Fab')) # create silscreen #mod.append(kmt.RectLine(start=[-2, -2], end=[5, 2], layer='F.SilkS')) (minx, miny, maxx, maxy) = self.boundingBox() w = maxx - minx h = maxy - miny ox = -maxx + w / 2 oy = -maxy + h / 2 # create courtyard mod.append( kmt.RectLine(start=[-w / 2, -h / 2], end=[w / 2, h / 2], layer='F.CrtYd')) #mod.append(kmt.FilledRect(start=[-w/2, -h/2], end=[w/2, h/2], layer='F.Mask')) n = 1 # iterate closed polygons for poly in self.closedPolygons: # find pads pads = [] for p in self.pads: if (p.distance(poly) < self.tolerance): pads.append(p) if len(pads) == 0: # poly primitive map = geo.mapping(poly) coords = [] for x, y in map['coordinates'][0]: coords.append([x + ox, -y - oy]) mod.append(kmt.Polygon(nodes=coords, layer='F.Cu', width=0)) else: # first pad is anchor anch = pads[0] # center point (pxmin, pymin, pxmax, pymax) = anch.bounds px = (pxmin + pxmax) / 2 py = (pymin + pymax) / 2 # size and rotation x1, y1 = anch.boundary.coords[0] x2, y2 = anch.boundary.coords[1] l = sqrt((x1 - x2)**2 + (y1 - y2)**2) rot = atan2((y1 - y2), (x1 - x2)) / pi * 180 # set pad rotation and transform polygon accordingly rot_poly = aff.rotate(poly, -rot, (px, py)) # poly primitive map = geo.mapping(rot_poly) coords = [] for x, y in map['coordinates'][0]: coords.append([x - px, -(y - py)]) kipoly = kmt.Polygon(nodes=coords) # create pad mod.append( kmt.Pad(number=n, type=kmt.Pad.TYPE_SMT, shape=kmt.Pad.SHAPE_CUSTOM, layers=['F.Cu'], at=[px + ox, -(py + oy)], size=[l, l], rotation=rot, primitives=[kipoly], anchor_shape=kmt.Pad.SHAPE_RECT)) # other pads are simple squares for i in range(1, len(pads)): # center point (pxmin, pymin, pxmax, pymax) = pads[i].bounds px = (pxmin + pxmax) / 2 + ox py = -((pymin + pymax) / 2 + oy) # size and rotation x1, y1 = pads[i].boundary.coords[0] x2, y2 = pads[i].boundary.coords[1] l = sqrt((x1 - x2)**2 + (y1 - y2)**2) rot = atan2((y1 - y2), (x1 - x2)) / pi * 180 mod.append( kmt.Pad(number=n, type=kmt.Pad.TYPE_SMT, shape=kmt.Pad.SHAPE_RECT, layers=['F.Cu'], at=[px, py], size=[l, l], rotation=rot)) # increase pad number n = n + 1 # output kicad model file_handler = kmt.KicadFileHandler(mod) file_handler.writeFile(filename)