/* * drl2gp.c - KiCad drill file to gnuplot converter * * Written 2010 by Werner Almesberger * Copyright 2010 Werner Almesberger * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. */ /* * KiCad drill files are in the Excellon format. The format is described here: * * http://www.excellon.com/manuals/program.htm * * Note that drl2gp currently only implements the subset necessary to process * the KiCad drill files encountered in a few projects, may not work correctly * for drill files from other projects, and will almost certainly fail in * tool-shattering ways when fed excellon files from other sources. */ #include #include #include #include #include #define MAX_ARGS 3 #define MAX_TOOL 10 #define MAX_STEP 0.01 /* max arc step, in mm */ static double tool_d[MAX_TOOL+1]; static FILE *out = NULL; static int lineno = 1; static int mill = 0; static double depth, d0, d1; #define IN2MM(in) ((in)*25.4) #define MIL2MM(mil) IN2MM((mil)/1000) static void header(void) { enum { ts_nl, /* at beginning of line */ ts_t, /* seen a ^T */ ts_metric, /* parsing METRIC,... */ ts_inch, /* parsing INCH,... */ ts_tc, /* seen ^T\d+C */ ts_skip, /* skip to next \n */ } state = ts_nl; int c, tool; double f = 1; double tmp; const char *next = NULL; int metric = 1; while ((c = getchar()) != EOF) { if (out) { if (fputc(c, out) == EOF) { perror("write"); exit(1); } } switch (state) { case ts_nl: switch (c) { case 'T': state = ts_t; tool = 0; break; case 'I': state = ts_inch; next = "NCH"; break; case 'M': state = ts_metric; next = "ETRIC"; break; case '%': return; case '\n': lineno++; break; default: state = ts_skip; break; } break; case ts_inch: if (c == ',') metric = 0; else { if (c == *next++) break; } state = ts_skip; break; case ts_metric: if (c == ',') metric = 1; else { if (c == *next++) break; } state = ts_skip; break; case ts_t: if (isdigit(c)) tool = tool*10+c-'0'; else if (c != 'C') state = ts_skip; else { assert(c != '\n'); if (tool > MAX_TOOL) { fprintf(stderr, "tool index %d too large (> %d)\n", tool, MAX_TOOL); exit(1); } tool_d[tool] = 0; f = 1; state = ts_tc; } break; case ts_tc: if (isdigit(c)) { tmp = c-'0'; if (!metric) tmp = IN2MM(tmp); if (f == 1) tool_d[tool] = tool_d[tool]*10+tmp; else { tool_d[tool] += f*tmp; f /= 10; } } else if (c == '.') { f = 0.1; } else if (c == '\n') { lineno++; state = ts_nl; } else { state = ts_skip; } break; default: if (c == '\n') { lineno++; state = ts_nl; } else { state = ts_skip; } } } } static double arc2angle(double arc, double r) { return acos(1-arc*arc/(r*r)/2); } static void half_circle(double cx, double cy, double rx, double ry, double s) { double m[4]; double x = rx, y = ry, tmp; double a; m[0] = cos(s); m[1] = -sin(s); m[2] = -m[1]; m[3] = m[0]; for (a = 0; a < M_PI; a += s) { printf("%f %f %f\n", cx+x, cy+y, -depth); tmp = x*m[0]+y*m[1]; y = x*m[2]+y*m[3]; x = tmp; } printf("%f %f %f\n", cx-rx, cy-ry, -depth); } static void slot(double xa, double ya, double xb, double yb, double d) { double dx = xb-xa; double dy = yb-ya; double cr = d/2; double tr = d0/2; double s = arc2angle(MAX_STEP, cr); double nx, ny; double f; assert(mill); f = (cr-tr)/hypot(dx, dy); nx = -dy*f; ny = dx*f; half_circle(xa, ya, nx, ny, s); half_circle(xb, yb, -nx, -ny, s); printf("%f %f %f\n\n", xa+nx, ya+ny, -depth); } static void circle(double cx, double cy, double d) { double cr = d/2; double tr = d0/2; double s = arc2angle(MAX_STEP, cr); double a; assert(mill); for (a = 0; a < 2*M_PI; a += s) printf("%f %f %f\n", cx+(cr-tr)*cos(a), cy+(cr-tr)*sin(a), -depth); printf("%f %f %f\n\n", cx+(cr-tr), cy, -depth); } static void drill(double x, double y) { printf("%f %f %f\n\n", x, y, -depth); } static void do_cmd(char cmd, double v, int nl) { static int active = 0; static int metric = 1; static int slotting = 0; static double x = 0, y = 0, x0 = 0, d = 0; int n = v; switch (cmd) { case 'M': switch (n) { case 30: /* end of program */ exit(0); case 47: /* operator message */ break; case 71: /* metric measuring mode */ metric = 1; break; case 72: /* inch measuring mode */ metric = 0; break; default: fprintf(stderr, "unrecognized command M%d at line %d\n", n, lineno); exit(1); } break; case 'G': switch (n) { case 5: /* drill mode */ break; case 85: /* slot */ x0 = x; slotting = 1; break; default: fprintf(stderr, "unrecognized command G%d at line %d\n", n, lineno); exit(1); } break; case 'T': if (!n) { active = 0; break; } if (n < 1 || n > MAX_TOOL || !tool_d[n]) { fprintf(stderr, "invalid tool T%d at line %d\n", n, lineno); exit(1); } d = tool_d[n]; if (mill) active = d >= d0; else active = d >= d0 && d <= d1; break; case 'X': x = metric ? v : IN2MM(v); break; case 'Y': if (!active) { slotting = 0; break; } if (!metric) v = IN2MM(v); if (slotting) { slot(x0, y, x, v, d); slotting = 0; break; } if (nl) { assert(d); if (d > d1) circle(x, v, d); else drill(x, v); break; } y = v; break; default: fprintf(stderr, "unrecognized command \"%c\" at line %d\n", cmd, lineno); exit(1); } } static void body(void) { char cmd = 0; double v = 0, f = 1; int c, s = 0; while ((c = getchar()) != EOF) { if (c == '\n') { lineno++; if (cmd) do_cmd(cmd, s ? -v : v, 1); cmd = 0; } else if (isdigit(c)) { if (f == 1) v = v*10+c-'0'; else { v += f*(c-'0'); f /= 10; } } else if (c == '.') { f = 0.1; } else if (c == '-') { s = !s; } else { if (cmd) do_cmd(cmd, s ? -v : v, 0); cmd = c; v = 0; f = 1; s = 0; } } } static void usage(const char *name) { fprintf(stderr, "usage: %s depth drill_d_min drill_d_max\n" "usage: %s depth mill_d\n\n" " options can be placed anywhere. -i and -m can be repeated.\n\n" " -i set units to imperial (mil)\n" " -m set units to metric (mm, default)\n" " -f out-file write filtered output to out-file\n" , name, name); exit(1); } int main(int argc, char **argv) { const char *filtered = NULL; int metric = 1; double arg[MAX_ARGS]; int n_arg = 0; char *end; double tmp; int i; for (i = 1; i != argc; i++) { if (*argv[i] != '-') { if (n_arg == MAX_ARGS) usage(*argv); tmp = strtod(argv[i], &end); if (*end) usage(*argv); if (!metric) tmp = MIL2MM(tmp); arg[n_arg++] = tmp; continue; } switch (argv[i][1]) { case 'i': if (argv[i][2]) usage(*argv); metric = 0; break; case 'm': if (argv[i][2]) usage(*argv); metric = 1; break; case 'f': if (filtered) usage(*argv); if (argv[i][2]) filtered = argv[i]+2; else { i++; if (i == argc) usage(*argv); filtered = argv[i]; } break; default: usage(*argv); } } depth = arg[0]; d0 = arg[1]; d1 = arg[2]; switch (n_arg) { case 2: d1 = d0; mill = 1; break; case 3: mill = 0; break; default: usage(*argv); } header(); body(); return 0; }