From e50a13cd31642fdcf183c8043e8e1d3764dc4df7 Mon Sep 17 00:00:00 2001 From: Bdale Garbee Date: Sun, 5 Jan 2020 22:19:41 -0700 Subject: [PATCH] move to python3 explicitly, per Debian policy --- packages/11071.py | 46 ++++- packages/fplht.py | 426 +++++++++++++++++++++++++++++++++++++--------- 2 files changed, 392 insertions(+), 80 deletions(-) diff --git a/packages/11071.py b/packages/11071.py index ac1e282..6271e6f 100755 --- a/packages/11071.py +++ b/packages/11071.py @@ -1,4 +1,4 @@ -#! /usr/bin/python +#! /usr/bin/python3 # # generate lihata footprint for 4ucon 11071 tabbed micro USB connector # Copyright 2020 by Bdale Garbee @@ -16,9 +16,53 @@ import math from fplht import * +# dimensions taken from 4ucon part drawing +# note that we define Y origin to be the board edge, not center mass + +pad_width = 0.4 +pad_height = 1.35 +pad_spacing = 0.65 +pad_y = 1.45 + 3.35 - pad_height/2 + +tab_width = 1.9 +tab_height = 1.9 +tab_x = 0.2 + tab_width / 2 +tab_y = 1.45 + +slot_x = 6.40 / 2 +slot_y = 1.45 +slot_width = 0.45 +slot_height = 1.55 +slot_copper = (2.15 - 1.55) / 2 + +wing_width = 1.6 +wing_height = 1.4 +wing_x = 3.2 +wing_y = 1.45 + 2.25 + fp = footprint() fp.name = "11071" fp.description = "4ucon 11071 tabbed micro USB connector" +fp.pad({'x' : pad_spacing* 2, 'y' : pad_y, 'width' : pad_width, 'height' : pad_height, 'name' : 'VBUS', 'number' : '1'}) +fp.pad({'x' : pad_spacing* 1, 'y' : pad_y, 'width' : pad_width, 'height' : pad_height, 'name' : 'D-', 'number' : '2'}) +fp.pad({'x' : pad_spacing* 0, 'y' : pad_y, 'width' : pad_width, 'height' : pad_height, 'name' : 'D+', 'number' : '3'}) +fp.pad({'x' : pad_spacing* -1, 'y' : pad_y, 'width' : pad_width, 'height' : pad_height, 'name' : 'HS', 'number' : '4'}) +fp.pad({'x' : pad_spacing* -2, 'y' : pad_y, 'width' : pad_width, 'height' : pad_height, 'name' : 'GND', 'number' : '5'}) + +# mounting "wings", or tabs next to the signal pads + +fp.pad({'x' : -wing_x, 'y' : wing_y, 'width' : wing_width, 'height' : wing_height, 'spacing' : 0, 'name' : "", 'number' : "G"}) +fp.pad({'x' : wing_x, 'y' : wing_y, 'width' : wing_width, 'height' : wing_height, 'spacing' : 0, 'name' : "", 'number' : "G"}) + +# mounting tabs under package + +fp.pad({'x' : -tab_x, 'y' : tab_y, 'width' : tab_width, 'height' : tab_height, 'spacing' : 0, 'name' : "", 'number' : "G"}) +fp.pad({'x' : tab_x, 'y' : tab_y, 'width' : tab_width, 'height' : tab_height, 'spacing' : 0, 'name' : "", 'number' : "G"}) + +# slots for through-hole mounting tabs +fp.slot({'x' : -slot_x, 'y' : slot_y, 'width' : slot_width, 'height' : slot_height, 'thickness' : slot_width + slot_copper * 2, 'name' : 'GND', 'number' : 5}) +fp.slot({'x' : slot_x, 'y' : slot_y, 'width' : slot_width, 'height' : slot_height, 'thickness' : slot_width + slot_copper * 2, 'name' : 'GND', 'number' : 5}) + fp.emit() diff --git a/packages/fplht.py b/packages/fplht.py index dea8c68..1a15590 100644 --- a/packages/fplht.py +++ b/packages/fplht.py @@ -1,4 +1,4 @@ -#! /usr/bin/python +#! /usr/bin/python3 # Python library to assist in generating lihata format footprints for pcb-rnd # Copyright 2020 by Bdale Garbee # @@ -17,104 +17,372 @@ # The footprint is delivered to stdout, so the encapsulating Makefile and/or # iterating wrapper script is responsible for redirecting to the desired # .lht file(s). +# +# all dimensions are assumed to be in mm import math import hashlib +def mils2mm(mils): + return 25.4 * (float(mils) / 1000) + class footprint(object): def __init__(self): - self.units = "mm" - self.name = "" - self.description = "" - self.dist_license = "GPLv3" - self.use_license = "Unlimited" - self.author = "Bdale Garbee " - self.cnt = 0 - - # primitive objects - # (proposed argument lists taken from pcb land_patterns doc) - # -- can probably collapse sflags+nflags->flags? - - # the basic plan is that each primitive function puts the object - # definition in the relevant dictionary .. when we go to emit the - # part, we'll do things like work out the minimum set of padstack - # prototypes by iterating over the dictionaries... then output the - # working set of required objects - - ## for copper layers - # def pad(): - # x1, y1, x2, y2, thickness, clearance, mask, name, number, sflags, nflags - # def pin(): - # x, y, thickness, clearance, mask, drill, name, number, sflags, nflags - # def slot(): + self.name = "" + self.description = "" + self.dist_license = "GPLv3" + self.use_license = "Unlimited" + self.author = "Bdale Garbee " + self.cnt = 0 + self.items = [] + self.padstacks = dict() + + # process defaults, based on greatest process minimum at our vendors + + self.process_soldermask = mils2mm(3); # space between pad and mask + self.process_trace = mils2mm(6); # trace width + self.process_space = mils2mm(6); # spacing + self.process_drill = mils2mm(13); # drill dize + self.process_ring = mils2mm(7); # annular ring + + self.line_thickness = mils2mm(10); # default silk line width + + # the basic plan is that each primitive function puts the object + # definition in the relevant dictionary .. when we go to emit the + # part, we'll do things like work out the minimum set of padstack + # prototypes by iterating over the dictionaries... then output the + # working set of required objects + + def pad(self,d): + # we expect these to be provided + # + # x center of pad + # y + # width size of pad + # height + # name + # number + # + # the following are normally computed / defaulted, but can be provided + # + # x1 start of pad "line" + # y1 + # x2 end of pad "line" + # y2 + # thickness thickness of pad "line" + # spacing space between pad and other traces + # soldermask space between pad and solder mask + # clearance twice the spacing between pad and other traces + # mask thickness of pad and solder mask + # options default to 'square' + + print ("adding pad") + d['type'] = 'pad' + + if 'x1' not in d: + d['x1'] = d['x'] - max(0, (d['width'] - d['height']) /2) + if 'x2' not in d: + d['x2'] = d['x'] + max(0, (d['width'] - d['height']) /2) + if 'y1' not in d: + d['y1'] = d['y'] - max(0, (d['height'] - d['width']) /2) + if 'y2' not in d: + d['y2'] = d['y'] + max(0, (d['height'] - d['width']) /2) + if 'thickness' not in d: + d['thickness'] = min(d['width'], d['height']) + if 'spacing' not in d: + d['spacing'] = self.process_space + if 'soldermask' not in d: + d['soldermask'] = self.process_soldermask + if 'clearance' not in d: + d['clearance'] = d['spacing'] / 2 + if 'mask' not in d: + d['mask'] = d['thickness'] + d['soldermask'] * 2 + if 'options' not in d: + d['options'] = 'square' + + self.items = self.items + [d] + + def pin(): + # we expect these to be provided + # + # x center of pin + # y + # drill diameter of drill hole + # name + # number + # + # the following are normally computed / defaulted, but can be provided + # + # ring width of annular ring around hole + # spacing space between pin and other traces + # soldermask space between pin and solder mask + # thickness thickness of pin "line" + # clearance twice the spacing between pad and other traces + # mask thickness of pad and solder mask + # options default to 'square' for pin number 1 + + print ("adding pin") + d['type'] = 'pin' + + if 'spacing' not in d: + d['spacing'] = self.process_space + if 'ring' not in d: + d['ring'] = self.process_ring + if 'soldermask' not in d: + d['soldermask'] = self.process_soldermask + if 'clearance' not in d: + d['clearance'] = d['spacing'] * 2 + if 'mask' not in d: + d['mask'] = d['thickness'] + d['soldermask'] * 2 + if 'options' not in d: # default pin 1 to square ring + if d['number'] == '1': + d['options'] = 'square' + else: + d['options'] = '' + if 'thickness' not in d: + d['thickness'] = d['drill'] + d['ring'] * 2 + if 'mask' not in d: + d['mask'] = d['thickness'] + d['soldermask'] * 2 + + self.items = self.items + [d] + + def slot(self,d): + # we expect these to be provided + # + # x center of slot + # y + # width size of slot + # height + # name + # number + # + # the following are normally computed / defaulted, but can be provided + # + # x1 start of slot "line" + # y1 + # x2 end of slot "line" + # y2 + # spacing space between slot copper and other traces + # thickness width of copper edge to edge across slot + # soldermask space between slot copper and solder mask + # clearance twice the spacing between slot copper and other traces + # mask thickness of slot copper and solder mask + + print ("adding slot") + d['type'] = 'slot' + + if 'x1' not in d: + d['x1'] = d['x'] - max(0, (d['width'] - d['height']) /2) + if 'x2' not in d: + d['x2'] = d['x'] + max(0, (d['width'] - d['height']) /2) + if 'y1' not in d: + d['y1'] = d['y'] - max(0, (d['height'] - d['width']) /2) + if 'y2' not in d: + d['y2'] = d['y'] + max(0, (d['height'] - d['width']) /2) + if 'spacing' not in d: + d['spacing'] = self.process_space + if 'thickness' not in d: + d['thickness'] = d['width'] + self.process_ring * 2 + if 'soldermask' not in d: + d['soldermask'] = self.process_soldermask + if 'clearance' not in d: + d['clearance'] = d['spacing'] / 2 + if 'mask' not in d: + d['mask'] = d['thickness'] + d['soldermask'] * 2 + + self.items = self.items + [d] ## for silk layers - # def line(): - # x1, y1, x2, y2, thickness - # def arc(): - # x, y, width, height, startangle, deltaangle, thickness + def line(): + # we expect these to be provided + # + # x1 start of silk line + # y1 + # x2 end of silk line + # y2 + # + # the following are normally defaulted, but can be provided + # + # thickness thickness of silk line + + print ("adding line") + d['type'] = 'line' + + if 'thickness' not in d: + d['thickness'] = self.line_thickness + + self.items = self.items + [d] + + def arc(): + # we expect these to be provided + # + # x center of silk arc + # y + # width + # height + # startangle + # deltaangle + # + # the following are normally defaulted, but can be provided + # + # thickness thickness of silk line + + print ("adding arc") + d['type'] = 'arc' + + if 'thickness' not in d: + d['thickness'] = self.line_thickness + + self.items = self.items + [d] + + def write_pin_square(size): + print(" li:ps_poly {") + print(" %u mm" % -size / 2) + print(" %u mm" % -size / 2) + print(" %u mm" % size / 2) + print(" %u mm" % -size / 2) + print(" %u mm" % size / 2) + print(" %u mm" % size / 2) + print(" %u mm" % -size / 2) + print(" %u mm" % size / 2) + print(" }") + + def write_pin_circle(size): + print(" ha:ps_circ {") + print(" x = 0") + print(" y = 0") + print(" dia = $u mm" % size) + print(" }") + + def write_pin_shape(p, layer): + print(" ha:ps_shape_v4 {") + print(" clearance = %u mm" % p['clearance']) + if p['options'] == 'square': + write_pin_square(p['copper']) + else: + write_pin_circle(p['copper']) + print(" ha:layer_mask {") + print(" copper = 1") + print(" $s = 1" % layer) + print(" }") + print(" }") def emit_padstack_prototypes(self): - print("prototypes will go here") + # iterate over copper items creating min set of padstack prototypes, + # and annotating items with their prototype number as we go + protocount = 0 + for i in self.items: + if i['type'] == 'pad': + myhash = i['type']+str(i['width'])+str(i['height'])+str(i['thickness'])+str(i['spacing'])+str(i['soldermask'])+str(i['clearance'])+str(i['mask'])+i['options'] + if i['type'] == 'slot': + myhash = i['type']+str(i['width'])+str(i['height'])+str(i['thickness'])+str(i['spacing'])+str(i['soldermask'])+str(i['clearance'])+str(i['mask']) + if i['type'] == 'pin': + myhash = i['type']+str(i['drill'])+str(i['ring'])+str(i['spacing'])+str(i['soldermask'])+str(i['clearance'])+str(i['mask'])+i['options'] - def emit_padstacks(self): - print("padstacks will go here") + # print ("myhash is %s" % myhash) + if myhash in self.padstacks: + p = self.padstacks[myhash] + i['prototypenumber'] = p['prototypenumber'] + else: + # print ("new hash!") + i['prototypenumber'] = protocount + protocount += 1 + self.padstacks[myhash] = i - def emit_top_silk(self): - print("top_silk will go here") + # print ("%u unique prototypes found" % protocount) + + # now that we have the set of prototypes, emit them + print (" li:padstack_prototypes {") + for thisp in self.padstacks: + p = self.padstacks[thisp] + if p['type'] == 'pin': + print(" ha:ps_proto_v4.%u {" % p['prototypenumber']) + print(" htop = 0") + print(" hbottom = 0") + print(" hplated = 1") + print(" hdia = %u mm" % p['drill']) + print(" li:shape {") + write_stack_shape(p, 'top') + write_stack_shape(p, 'bottom') + print(" }") + print(" }") + + if p['type'] == 'pad': + print(" ha:ps_proto_v4.%u {" % p['prototypenumber']) + print(" htop = 0") + print(" hbottom = 0") + print(" hplated = 1") + print(" hdia = %u mm" % 0) # bogus to the max + print(" li:shape {") + print(" }") + print(" }") + + print (" }") + + def emit_padstacks(self): + print("padstacks will go here") + # process pad, pin, slot items referencing relevant prototypes + print("%u items defined" % len(self.items)) + print("----- start of defined items -----") + print(self.items) + print("----- end of defined items -----") def emit_line(self, role, id, x1, y1, x2, y2, unit): - print(" ha:line.%u {" % id) - print(" clearance = 0") - print(" thickness = 0") - print(" ha:attributes {") - print(" subc-role = %s" % role) - print(" }") - print(" x1 = %u%s" % (x1, unit)) - print(" y1 = %u%s" % (y1, unit)) - print(" x2 = %u%s" % (x2, unit)) - print(" y2 = %u%s" % (y2, unit)) - print(" }") - self.cnt = self.cnt + 1 + print(" ha:line.%u {" % id) + print(" clearance = 0") + print(" thickness = 0") + print(" ha:attributes {") + print(" subc-role = %s" % role) + print(" }") + print(" x1 = %u%s" % (x1, unit)) + print(" y1 = %u%s" % (y1, unit)) + print(" x2 = %u%s" % (x2, unit)) + print(" y2 = %u%s" % (y2, unit)) + print(" }") + self.cnt = self.cnt + 1 + + def emit_top_silk(self): + print("top_silk will go here") + # process line and arc items def emit_bbox(self): - self.cnt = 0 - print(" ha:subc-aux {") - print(" lid = 1") - print(" ha:type {") - print(" top = 1") - print(" misc = 1") - print(" virtual = 1") - print(" }") - print(" li:objects {") + self.cnt = 0 + print(" ha:subc-aux {") + print(" lid = 1") + print(" ha:type {") + print(" top = 1") + print(" misc = 1") + print(" virtual = 1") + print(" }") + print(" li:objects {") self.emit_line("origin", self.cnt, 0, 0, 0, 0, "mm"); self.emit_line("x", self.cnt, 0, 0, 1, 0, "mm"); self.emit_line("y", self.cnt, 0, 0, 0, 1, "mm"); - print(" }") - print(" }") + print(" }") + print(" }") def create_uuid(self): - return hashlib.md5(self.name).hexdigest() + return hashlib.md5(self.name.encode('utf-8')).hexdigest() def emit(self): - print("li:pcb-rnd-subcircuit-v4 {") - print(" ha:subc.0 {") - print(" ha:attributes {") - print(" description = %s" % self.description) - print(" dist_license = %s" % self.dist_license) - print(" use_license = %s" % self.use_license) - print(" author = %s" % self.author) - print(" }") - print(" uid = %s" % self.create_uuid()) - print(" ha:data {") - self.emit_padstack_prototypes() - print(" li:objects {") - self.emit_padstacks() - print(" }") - print(" li:layers {") - self.emit_top_silk() - self.emit_bbox() - print(" }") - print(" }") - print(" }") - print("}") + print("li:pcb-rnd-subcircuit-v4 {") + print(" ha:subc.0 {") + print(" ha:attributes {") + print(" description = %s" % self.description) + print(" dist_license = %s" % self.dist_license) + print(" use_license = %s" % self.use_license) + print(" author = %s" % self.author) + print(" }") + print(" uid = %s" % self.create_uuid()) + print(" ha:data {") + self.emit_padstack_prototypes() + print(" li:objects {") + self.emit_padstacks() + print(" }") + print(" li:layers {") + self.emit_top_silk() + self.emit_bbox() + print(" }") + print(" }") + print(" }") + print("}") -- 2.47.2