From 3a5dbcf1e13704b7352d5fb3c4777d7df3fed081 Mon Sep 17 00:00:00 2001 From: Hamilton Kibbe Date: Sun, 28 Sep 2014 21:17:13 -0400 Subject: added ExcellonFile class --- gerber/utils.py | 108 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 66 insertions(+), 42 deletions(-) (limited to 'gerber/utils.py') diff --git a/gerber/utils.py b/gerber/utils.py index 35b4fd0..625a9e1 100644 --- a/gerber/utils.py +++ b/gerber/utils.py @@ -10,28 +10,29 @@ files. """ # Author: Hamilton Kibbe -# License: +# License: + def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'): """ Convert gerber/excellon formatted string to floating-point number - + .. note:: - Format and zero suppression are configurable. Note that the Excellon - and Gerber formats use opposite terminology with respect to leading - and trailing zeros. The Gerber format specifies which zeros are - suppressed, while the Excellon format specifies which zeros are - included. This function uses the Gerber-file convention, so an - Excellon file in LZ (leading zeros) mode would use - `zero_suppression='trailing'` - - + Format and zero suppression are configurable. Note that the Excellon + and Gerber formats use opposite terminology with respect to leading + and trailing zeros. The Gerber format specifies which zeros are + suppressed, while the Excellon format specifies which zeros are + included. This function uses the Gerber-file convention, so an + Excellon file in LZ (leading zeros) mode would use + `zero_suppression='trailing'` + + Parameters ---------- value : string A Gerber/Excellon-formatted string representing a numerical value. format : tuple (int,int) - Gerber/Excellon precision format expressed as a tuple containing: + Gerber/Excellon precision format expressed as a tuple containing: (number of integer-part digits, number of decimal-part digits) zero_suppression : string @@ -41,12 +42,12 @@ def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'): ------- value : float The specified value as a floating-point number. - + """ # Format precision integer_digits, decimal_digits = format MAX_DIGITS = integer_digits + decimal_digits - + # Absolute maximum number of digits supported. This will handle up to # 6:7 format, which is somewhat supported, even though the gerber spec # only allows up to 6:6 @@ -59,40 +60,39 @@ def parse_gerber_value(value, format=(2, 5), zero_suppression='trailing'): negative = '-' in value if negative: value = value.strip(' -') - + # Handle excellon edge case with explicit decimal. "That was easy!" if '.' in value: return float(value) - + digits = [digit for digit in '0' * MAX_DIGITS] offset = 0 if zero_suppression == 'trailing' else (MAX_DIGITS - len(value)) for i, digit in enumerate(value): digits[i + offset] = digit - - + result = float(''.join(digits[:integer_digits] + ['.'] + digits[integer_digits:])) return -1.0 * result if negative else result - - + + def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'): """ Convert a floating point number to a Gerber/Excellon-formatted string. - + .. note:: - Format and zero suppression are configurable. Note that the Excellon - and Gerber formats use opposite terminology with respect to leading - and trailing zeros. The Gerber format specifies which zeros are - suppressed, while the Excellon format specifies which zeros are - included. This function uses the Gerber-file convention, so an - Excellon file in LZ (leading zeros) mode would use + Format and zero suppression are configurable. Note that the Excellon + and Gerber formats use opposite terminology with respect to leading + and trailing zeros. The Gerber format specifies which zeros are + suppressed, while the Excellon format specifies which zeros are + included. This function uses the Gerber-file convention, so an + Excellon file in LZ (leading zeros) mode would use `zero_suppression='trailing'` - + Parameters ---------- value : float A floating point value. format : tuple (n=2) - Gerber/Excellon precision format expressed as a tuple containing: + Gerber/Excellon precision format expressed as a tuple containing: (number of integer-part digits, number of decimal-part digits) zero_suppression : string @@ -106,12 +106,12 @@ def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'): # Format precision integer_digits, decimal_digits = format MAX_DIGITS = integer_digits + decimal_digits - + if MAX_DIGITS > 13 or integer_digits > 6 or decimal_digits > 7: raise ValueError('Parser only supports precision up to 6:7 format') - + # negative sign affects padding, so deal with it at the end... - negative = value < 0.0 + negative = value < 0.0 if negative: value = -1.0 * value @@ -119,48 +119,72 @@ def write_gerber_value(value, format=(2, 5), zero_suppression='trailing'): fmtstring = '%%0%d.0%df' % (MAX_DIGITS + 1, decimal_digits) digits = [val for val in fmtstring % value if val != '.'] - - # Suppression... + + # Suppression... if zero_suppression == 'trailing': while digits[-1] == '0': digits.pop() else: while digits[0] == '0': digits.pop(0) - + return ''.join(digits) if not negative else ''.join(['-'] + digits) - def decimal_string(value, precision=6): """ Convert float to string with limited precision - + Parameters ---------- value : float A floating point value. - precision : + precision : Maximum number of decimal places to print Returns ------- value : string The specified value as a string. - + """ floatstr = '%0.20g' % value integer = None decimal = None if '.' in floatstr: - integer, decimal = floatstr.split('.') + integer, decimal = floatstr.split('.') elif ',' in floatstr: - integer, decimal = floatstr.split(',') + integer, decimal = floatstr.split(',') if len(decimal) > precision: decimal = decimal[:precision] if integer or decimal: return ''.join([integer, '.', decimal]) else: return int(floatstr) - + +def detect_file_format(filename): + """ Determine format of a file + + Parameters + ---------- + filename : string + Filename of the file to read. + + Returns + ------- + format : string + File format. either 'excellon' or 'rs274x' + """ + + # Read the first 20 lines + with open(filename, 'r') as f: + lines = [next(f) for x in xrange(20)] + + # Look for + for line in lines: + if 'M48' in line: + return 'excellon' + elif '%FS' in line: + return'rs274x' + return 'unknown' -- cgit