[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
Re: gEDA-user: Importing board outline from .emn file
Hi Folks,
I have just finished writing a Python script to import mechanical data
(from ProEng) into pcb and thought I would offer it up to the community
if anyone has a similar need.
The script is attached and it is used in the following way:-
> python importEmn.py emnFile pcbFile_in pcbFile_out
where:-
emnFile is the emn file being imported with the mechanical data. It is
structured following the IDF3.0 standard referenced below
www.aertia.com/docs/priware/IDF_V30_Spec.pdf
pcbFile_in is the original pcb file that will not be modified
pcbFile-out is a copy of pcbFile_in with the mechanical information
added to the appropriate layers (if they exist)
At this stage it supports the import of outline, keepouts and drill data
but this could be expanded if there is any interest.
Cheers, Neil
On Wed, 2009-02-04 at 23:08 -0500, Neil Webster wrote:
> Hi Folks,
>
> Does anyone know if there is a way to import a board outline defined in
> an emn file into PCB? The emn file is produced by ProEng 3-D CAD tool
> and it seems quite simply structured. I have attached it below ...
>
> .HEADER
> BOARD_FILE 3.0 "Pro/ENGINEER TM Wildfire 3.0 (c" 2009/02/04.13:30:43
> 3
> PCB-DB MM
> .END_HEADER
> .BOARD_OUTLINE MCAD
> 1.20000
> 0 0.00000 0.00000 0.00000
> 0 60.00000 0.00000 0.00000
> 0 60.00000 30.00000 0.00000
> 0 0.00000 30.00000 0.00000
> 0 0.00000 0.00000 0.00000
> .END_BOARD_OUTLINE
> .DRILLED_HOLES
> 3.50000 21.50000 16.00000 NPTH BOARD PIN
> MCAD
> .END_DRILLED_HOLES
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 7.83200 30.00000 0.00000
> 0 7.83200 27.99400 0.00000
> 0 10.83200 27.99400 0.00000
> 0 10.83200 30.00000 0.00000
> 0 7.83200 30.00000 0.00000
> .END_PLACE_KEEPOUT
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 49.14500 30.00000 0.00000
> 0 49.14500 27.99400 0.00000
> 0 52.14500 27.99400 0.00000
> 0 52.14500 30.00000 0.00000
> 0 49.14500 30.00000 0.00000
> .END_PLACE_KEEPOUT
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 10.84300 0.00000 0.00000
> 0 7.84300 0.00000 0.00000
> 0 7.84300 2.00600 0.00000
> 0 10.84300 2.00600 0.00000
> 0 10.84300 0.00000 0.00000
> .END_PLACE_KEEPOUT
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 52.12000 0.00000 0.00000
> 0 49.12000 0.00000 0.00000
> 0 49.12000 2.00600 0.00000
> 0 52.12000 2.00600 0.00000
> 0 52.12000 0.00000 0.00000
> .END_PLACE_KEEPOUT
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 0.00000 18.87600 0.00000
> 0 0.00000 15.87600 0.00000
> 0 2.00600 15.87600 0.00000
> 0 2.00600 18.87600 0.00000
> 0 0.00000 18.87600 0.00000
> .END_PLACE_KEEPOUT
> .PLACE_KEEPOUT UNOWNED
> TOP 0.00000
> 0 57.99400 18.88100 0.00000
> 0 60.00000 18.88100 0.00000
> 0 60.00000 15.88100 0.00000
> 0 57.99400 15.88100 0.00000
> 0 57.99400 18.88100 0.00000
> .END_PLACE_KEEPOUT
>
>
>
>
>
>
> _______________________________________________
> geda-user mailing list
> geda-user@xxxxxxxxxxxxxx
> http://www.seul.org/cgi-bin/mailman/listinfo/geda-user
>
>
#author: Neil Webster
#data: 11 February 2009
#!/usr/bin/env python
#this script imports an emn file into a pcb file
#to import outline information, the pcb file must already have an "outline" layer defined
#to import keepout information, the pcb file must already have a "keepout" layer defined
#usage python importEmn.py emnFile pcbFile_in pcbFile_out
# emnFile is the emn file being imported with the mechanical data
# pcbFile_in is the original pcb file that will not be modified
# pcbFile-out is a copy of pcbFile_in with the machanical infomation added
#the emnfile format is described in Intermediate Data Format (IDF3.0) standard
import sys
#set up constants to convert to PCB units of 1/100,000 inch
#IDF3.0 files are defined in "MM" or "THOU"
mmToPcb = 0.0393700787 * 100000
thouToPcb = 100
class Point:
#this is an IDF3.0 point as defined in Record3 of section 3.2
def __init__(self, recordString):
parts = recordString.split()
self.loopLabel = int(parts[0])
self.xCoordinateOfPoint = float(parts[1])
self.yCoordinateOfPoint = float(parts[2])
self.includeAngle = float(parts[3])
class Section:
#the Section class holds the records and associated fields of an IDF3.0 section
#each section has its own defined fields and the constructor creates the correct fields
#dependant on the type of section being defined
def __init__(self, sectionText):
#the contructor is passed a section of text extracted from the file bounded by begin and end tags
if "HEADER" in sectionText[0]:
#see section 3.1 of IDF3.0
s1 = sectionText[0].split()
self.type = s1[0].lstrip('.').upper()
s2 = splitRecord(sectionText[1])
self.fileType = s2[0]
self.idfVersionNumber = float(s2[1])
self.sourceSystemId = s2[2]
self.date = s2[3]
self.boardFileVersion = int(s2[4])
s3 = sectionText[2].split()
self.boardName = s3[0]
self.unitsDefinition = s3[1]
elif "BOARD_OUTLINE" in sectionText[0]:
#see section 3.2 of IDF3.0
s1 = sectionText[0].split()
self.type = s1[0].lstrip('.').upper()
self.outlineOwner = s1[1]
self.thickness = float(sectionText[1])
self.outlinePath = []
pathText = sectionText[2:-1]
for i in pathText:
self.outlinePath.append(Point(i))
elif "PLACE_KEEPOUT" in sectionText[0]:
#see section 3.8 of IDF3.0
s1 = sectionText[0].split()
self.type = s1[0].lstrip('.').upper()
self.keepoutOwner = s1[1]
s2 = sectionText[1].split()
self.boardSide = s2[0]
self.keepoutHeight = float(s2[1])
self.keepoutPath = []
pathText = sectionText[2:-1]
for i in pathText:
self.keepoutPath.append(Point(i))
elif "DRILLED_HOLES" in sectionText[0]:
#see section3.10 of IDF3.0
s1 = sectionText[0].split()
self.type = s1[0].lstrip('.').upper()
s2 = sectionText[1].split()
self.holeDiameter = float(s2[0])
self.xCoordinateOfHole = float(s2[1])
self.yCoordinateOfHole = float(s2[2])
self.platingStyle = s2[3]
self.associatedPart = s2[4]
self.holeType = s2[5]
self.holeOwner = s2[6]
else:
print "section not defined"
def splitRecord(recordString):
#find any literal strings embedded within the recordString
interim = recordString.split('"')
if len(interim) == 1: #there are no literal strings.
return recordString.split() #regular split is fine
if len(interim) == 3: #there is one literal string and it must be in the middle
#split the first and last elements and add the middle (literal string) intact
return interim[0].split() + [interim[1]] + interim[2].split()
#read in the emn file which is the first argument
f = open(sys.argv[1])
emn = f.readlines()
f.close()
#remove any comment lines
emn = [l for l in emn if not l.startswith("#")]
#find the keyword lines
keywords = [l for l in emn if l.startswith(".")]
#break the keywords into pairs in separate lists, even in beginKeywrods; odd in endKeywords
beginKeywords = keywords[::2]
endKeywords = keywords[1::2]
#create a list to hold an object oriented representation of each of the emn file sections
sections = []
while len(beginKeywords)>0:
b = beginKeywords[0]
e = endKeywords[0]
#extract the text block between the begin and end keywords
sectionText = emn[emn.index(b):emn.index(e)+1]
#add a new section to the end of the list
sections.append(Section(sectionText))
#remove the processed elements
beginKeywords.remove(b)
endKeywords.remove(e)
for i in sectionText:
emn.remove(i)
def createPcbLine(point1,point2,units):
#this takes 2x IDF3.0 points object and returns a pcb Line as a string
#initially just supports line and not arc or cutouts
#make tuples of the 2 points
p1 = (point1.xCoordinateOfPoint, point1.yCoordinateOfPoint)
p2 = (point2.xCoordinateOfPoint, point2.yCoordinateOfPoint)
if units == "MM":
conv = mmToPcb
elif units == "THOU":
conv = thouToPcb
#convert the tuples to the pcb units
p1 = [int(x*conv) for x in p1]
p2 = [int(x*conv) for x in p2]
#dealing with straight line segments therefore loopLabel=0 and includeAngle=0 but these are not checked yet
#note that line thickness is set to 1000 and clearance to 2000, flags set to ""
return "\tLine[" + str(p1[0]) + " " + str(p1[1]) + " " + str(p2[0]) + " " + str(p2[1]) + " 1000 2000 \"\"]\n"
#read in the pcb file which is the second argument
f = open(sys.argv[2])
pcb = f.readlines()
f.close()
#find the Layer definitions
pcbLayers = [l for l in pcb if l.startswith("Layer")]
#find the exact index of the outline & keepout layers if they exist
outlineLayerIndex = 0
keepoutLayerIndex = 0
for l in pcbLayers:
if "outline" in l:
outlineLayerIndex = pcb.index(l)
if "keepout" in l:
keepoutLayerIndex = pcb.index(l)
#extract the units from the HEADER section, should only be one header but use first item in list
units = [s.unitsDefinition for s in sections if s.type == "HEADER"][0]
#if we have an outline layer then add the outline data
if outlineLayerIndex != 0:
#get the outline path from the BOARD_OUTLINE section. There should only be one but take the first anyway just in case
olp = [s.outlinePath for s in sections if s.type == "BOARD_OUTLINE"][0]
#IDF3.0 works with points and pcb works with lines i.e. a pair of adjacent point
#go through all points in the path except the last
for lp1 in olp[0:-1]:
#the second point of a pair is the next one in the list
lp2 = olp[olp.index(lp1)+1]
#insert after the Layer line and the next line which hold a (, hence increment of 2
pcb.insert(outlineLayerIndex+2,createPcbLine(lp1,lp2,units))
#if we have an keepout layer then add the keepout data
if keepoutLayerIndex != 0:
#pull out a list of keepout paths
allKeepouts = [s.keepoutPath for s in sections if s.type=="PLACE_KEEPOUT"]
for singleKeepout in allKeepouts: #for each path in the list
for lp1 in singleKeepout[0:-1]: #a single keepout is a closed collection of points
lp2 = singleKeepout[singleKeepout.index(lp1)+1]
#insert after the Layer line and the next line which hold a (, hence increment of 2
pcb.insert(keepoutLayerIndex+2,createPcbLine(lp1,lp2,units))
#add drilled holes
dh = [s for s in sections if s.type == "DRILLED_HOLES"]
if units == "MM":
conv = mmToPcb
elif units == "THOU":
conv = thouToPcb
#find where to insert the drill data in the pcb file, it should be just before the NetList section
drillDataIndex = pcb.index("NetList()\n")
for hole in dh:
x = int(conv*(hole.xCoordinateOfHole))
y = int(conv*(hole.yCoordinateOfHole))
diam = int(conv*(hole.holeDiameter))
pcb.insert(drillDataIndex,"Via[" + str(x) + " " + str(y) + " " + str(diam) + " 2000 0 " + str(diam) + """ "" "hole"]"""+"\n")
#write the newly created file
f = open(sys.argv[3],"w")
f.writelines(pcb)
f.close()
_______________________________________________
geda-user mailing list
geda-user@xxxxxxxxxxxxxx
http://www.seul.org/cgi-bin/mailman/listinfo/geda-user