move to python3 explicitly, per Debian policy
authorBdale Garbee <bdale@gag.com>
Mon, 6 Jan 2020 05:19:41 +0000 (22:19 -0700)
committerBdale Garbee <bdale@gag.com>
Mon, 6 Jan 2020 05:19:41 +0000 (22:19 -0700)
packages/11071.py
packages/fplht.py

index ac1e2821125f704a5b44a7746f600bc62a954b91..6271e6fcd927bb6cf7697507b79802fcadf4bbd4 100755 (executable)
@@ -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 <bdale@gag.com>
 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()
index dea8c6890bd0c63df99096f2ca3feb339a1bc01a..1a155903efe150d6d5e4710dfb66247340f24927 100644 (file)
@@ -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 <bdale@gag.com>
 #
 # The footprint is delivered to stdout, so the encapsulating Makefile and/or 
 # iterating wrapper script is responsible for redirecting to the desired 
 # <something>.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 <bdale@gag.com>"
-       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 <bdale@gag.com>"
+        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("}")