Friday, June 27, 2014

Fixing a Gerber Drill File using a Python Script

Adventures in getting sidetracked. I want to build a constant current load for capacity testing some SLA (Sealed Lead Acid) batteries that I bought for my UPS. My first stop when starting a project is usually a quick Google search to see what has already been done and might be available, possibly saving me a great deal of development and testing time. I soon found a printed circuit board design that looked like a good fit but there was a problem with the Gerber files needed  for fabrication. This post walks you though my experience in diagnosing the error, developing a solution and getting the boards fabricated.

Constant Current Load

This effort began with a search for a circuit to use as the basis for a constant current load. Google led me to the blog post below by cwhittenburg on the Sleepy Robot... blog. Whittenburg based this design on that of constant current load found on the EEVBlog with enhancements made to reduce cost and improve performance. I recommend reading both posts about his work on the constant current dummy load.
http://www.sleepyrobot.com/?cat=7

Here is a link for the constant current load project from the EEVBlog.
http://www.eevblog.com/2010/08/01/eevblog-102-diy-constant-current-dummy-load-for-power-supply-and-battery-testing/

SleepyRobot's revised Constant Current Dummy load. 

This seems like a good candidate pcb for my needs so I downloaded the Gerber files using the link at the bottom of his "Constant Current Dummy Load Revised" post.
http://www.sleepyrobot.com/wp-content/uploads/2012/01/currentload-rev2-gerbers.zip

You should always check your Gerber files before sending them off for fab, doubly so if you are getting the files from someone else. There are several ways to view Gerber files. I used the website below.
http://www.gerber-viewer.com/default.aspx

Preview of gerber files reveals that the drill holes and copper pads are offset.

Whoa, the drill file does not line up with the copper layer! I checked the other layers (not shown) and everything but the drill strikes lined up well. PCB layout programs offer many options for formatting the Gerber output files and drill files and clearly an offset between the drill file and pcb layers crept in somehow. I tried to find contact information for the blog author to request a new set of Gerbers but had no luck. And I believed that a mistake in the zip file of Gerbers meant you have no option other than re-exporting the files from your development environment.

Back Burner

I moved on to other projects, planning to design my own or find another pcb to use for an active load. Eventually it occurred to me that unlike the Gerber layers, the drill file is just a text file that tells the drill machine which tool to load and where to drill the holes! I can work with that! I should be able to determine the offset of the drill holes and modify the text of the file to correct the drill hole locations. And once the offset is known, it should be a simple matter to write a script or program to make the changes to the drill file.

I used the same on-line tool to zoom in on the board and measure the distance between one of the drill holes and it's mating pcb pad. I used the largest, upper right corner pad as my reference. A quick measurement tells us that it appears to have the same offset for both X and Y, approximately 1.1822 inches.

Rough measurement of hold-pad offset using viewer.

I manually added 11822 (for 1.1822 inches) to the X and Y coordinate of a single drill strike and then compared the new location to the Gerber pad. Comparison required creating a new zip file for the Gerbers and viewing the layers from the new zip file using the Online Gerber-Viewer. Each time the drill hole got closer to the pad, I was able to zoom in more and get a more accurate measurement. Using this iterative process I found the actual offset is 1.1803 inches. Now to figure out how to modify the drill file to add 11803 to each X and Y value.

Analyzing the Drill File

The drill holes are made using the information in the file "currentload.TXT" from the zip file. Below is an excerpt from the drill file. We are interested in the text starting at T01 which shows the drill-hole coordinates for the first tool, T01. The remainder of the file has coordinates for the other drills used on the board. You can see that each drill strike starts with an X or a Y coordinate. Some of the lines have both X and Y coordinates. The lines with a single value reflect a hole being located by moving one axis while maintaining the other axis position. Note that "X" and "Y" appear in column 0 for each coordinate provided, with the second "Y" in column 7 in lines with both x and y coordinates. We can find the coordinates in the drill file by looking for "X" and "Y" in the file in the correct columns!

M48
;Layer_Color=9474304
;FILE_FORMAT=2:4
INCH
;TYPE=PLATED
T1F00S00C0.0320
T2F00S00C0.0335
T3F00S00C0.0354
T4F00S00C0.0394
T5F00S00C0.0400
T6F00S00C0.0870
T7F00S00C0.0900
;TYPE=NON_PLATED
T8F00S00C0.1000
T9F00S00C0.1142
%
T01
X037189Y011189
Y012189
Y015439
Y016439
X012539Y012189
X011539
X009789Y012689
X008789
X009289Y011939


Python Script

I have been looking for a reason to give Python try and this project seemed like a great opportunity. A few Google searches and I was on my way. My approach is to open the drill file and process the text file line by line while building a new output file, line by line as the values are modified. The script will check each line for "X" or "Y" in the correct locations. If no coordinates are found, I copy the line as is to the new file. If coordinates are found, I extract the values, add the offset and build a new line for the output file using the adjusted coordinate values.

For example, if the 8th character (item 7 since the the first item in the array is item 0 ) in a line is "Y" then that line has both X and Y values that must both have the offset added. I extract the old x value and the old y value and add the offset. Then I construct a new line using the correct x and y values and write the line to the output file. If no coordinates are found, the line is written to the output file without modification.

Below is the script I used to modify the drill file. Just select and copy into your own .py file to use the script. Each line in the script is commented to make it easier to follow. I used the script directly from my Python install folder and placed the drill file, currentload.txt in this case, in the same folder. Change the filename in the " i  =" line of the script if you want to change the name of the file to be processed. In the same manner, you can edit the "o =" of the script to change the name of the output file.

The most complicated part of the program is building the new line using the old value plus the offset. Looking at the last elif, we found a "Y" at position 0. We read the next six characters, 1 through 7 into the string oldy. Next we convert the oldy string to an integer, add the offset, convert the new number to a string and pad the string with leading zeros to six characters. Following this, the new line is written to the output file. Next we process the next line of the input file.

#Adds an offset to x and y drill hole locations in a pcb drill file
#Shane Trent - shanedtrent@gmail.com
#fettricks.blogspot.com

i = open ("currentload.txt", 'r') #Open file to be modified for reading
o = open ("currentloadnew.txt", 'w') #Open new output file for writing

xoff = 11803     #X offset measured to be added to each x value
yoff = 11803     #Y offset measured to be added to each y value

for line in i:   #For each line in the array check the conditions below
    if 7 == line.rfind('Y'):   #found a line with both X and Y
        oldx = line[1:7]       #extract old X
        oldy = line[8:15]      #extract old Y
        line = 'X' + str(xoff + int(oldx)).zfill(6) + 'Y' + str(yoff +int(oldy)).zfill(6) + '\n' #build new line
    elif 0 == line.rfind('X'): #found a line with only X
        oldx = line[1:7]       #extract old X
        line = 'X' + str(xoff + int(oldx)).zfill(6) + '\n'   #build new line     
    elif 0 == line.rfind('Y'): #found a line with only Y
        oldy = line[1:7]       #extract old Y
        line = 'Y' + str(yoff + int(oldy)).zfill(6) + '\n'   #build new line
    o.write(line)   #write this line to new file
    
i.close() #Close the input file
o.close() #Close the output file

New Gerber File

Below you can see the preview of the corrected Gerber files and a detailed view of the drill hole and pad being used as our reference. Note the three red dots on the left are mounting holes for the 9V battery holder and do not have copper pads.

The holes and pad now appear to be aligned.

Success! This magnified view of a hole/pad pair reveals very close alignment.

Board Fabrication

I planed to have the boards fabed at OSH Park (3 very nice boards for $5 per square inch) but then I realized the board is 16 square inches which would be $80 for three boards! For me this is a just-for-fun project, I was willing to spend $25 for a single board but $80 seemed a little high. Since I can wait on the boards, I decided to use this as an opportunity to try out one of the cheap overseas fabs. I selected iTead Studio and was able to order 10 boards delivered for $28.88 US. Here is a link to the fabrication package that I selected.
http://imall.iteadstudio.com/open-pcb/pcb-prototyping/im120418003.html

Here is the zip file of Gerbers that I send to be fabbed.
https://www.dropbox.com/s/lbwdg40l9ib7k3e/ConstCurrentFixed.zip

Fresh from the iTeadStudio fab.  The holes are aligned and the boards look great.


The finished boards look great and the drill holes line up nicely with the pads. I enjoyed learning how to use Python process text files and correct the drill file for the printed circuit board. I hope this information can be of use to you in your projects.