2010-12-08 02:14:49 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2010-12-08 10:26:42 +02:00
|
|
|
* KiCad drill files are in the Excellon format. The format is described here:
|
2010-12-08 02:14:49 +02:00
|
|
|
*
|
2010-12-08 10:26:42 +02:00
|
|
|
* http://www.excellon.com/manuals/program.htm
|
2010-12-08 02:14:49 +02:00
|
|
|
*
|
|
|
|
* 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 <stdlib.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <ctype.h>
|
2010-12-08 10:26:42 +02:00
|
|
|
#include <math.h>
|
2010-12-08 02:14:49 +02:00
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
|
|
|
|
#define MAX_ARGS 3
|
|
|
|
#define MAX_TOOL 10
|
|
|
|
|
2010-12-08 10:26:42 +02:00
|
|
|
#define MAX_STEP 0.01 /* max arc step, in mm */
|
|
|
|
|
2010-12-08 02:14:49 +02:00
|
|
|
|
|
|
|
static double tool_d[MAX_TOOL+1];
|
|
|
|
static FILE *out = NULL;
|
|
|
|
static int lineno = 1;
|
|
|
|
|
2010-12-08 10:26:42 +02:00
|
|
|
static int mill = 0;
|
2010-12-08 02:14:49 +02:00
|
|
|
static double depth, d0, d1;
|
|
|
|
|
|
|
|
|
|
|
|
#define IN2MM(in) ((in)*25.4)
|
|
|
|
#define MIL2MM(mil) IN2MM((mil)/1000)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* @@@ should at least detect INCH/METRIC
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void header(void)
|
|
|
|
{
|
|
|
|
enum {
|
|
|
|
ts_nl, /* at beginning of line */
|
|
|
|
ts_t, /* seen a ^T */
|
|
|
|
ts_tc, /* seen ^T\d+C */
|
|
|
|
ts_skip, /* skip to next \n */
|
|
|
|
} state = ts_nl;
|
|
|
|
int c, tool;
|
|
|
|
double f = 1;
|
2010-12-08 10:26:42 +02:00
|
|
|
double tmp;
|
2010-12-08 02:14:49 +02:00
|
|
|
|
|
|
|
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 '%':
|
|
|
|
return;
|
|
|
|
case '\n':
|
|
|
|
lineno++;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
state = ts_skip;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
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)) {
|
2010-12-08 10:26:42 +02:00
|
|
|
tmp = c-'0';
|
|
|
|
tmp = IN2MM(tmp);
|
2010-12-08 02:14:49 +02:00
|
|
|
if (f == 1)
|
2010-12-08 10:26:42 +02:00
|
|
|
tool_d[tool] =
|
|
|
|
tool_d[tool]*10+tmp;
|
2010-12-08 02:14:49 +02:00
|
|
|
else {
|
2010-12-08 10:26:42 +02:00
|
|
|
tool_d[tool] += f*tmp;
|
2010-12-08 02:14:49 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 10:52:58 +02:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2010-12-08 02:14:49 +02:00
|
|
|
static void slot(double xa, double ya, double xb, double yb, double d)
|
|
|
|
{
|
2010-12-08 10:52:58 +02:00
|
|
|
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;
|
|
|
|
|
2010-12-08 02:14:49 +02:00
|
|
|
assert(mill);
|
2010-12-08 10:52:58 +02:00
|
|
|
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);
|
2010-12-08 02:14:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void circle(double cx, double cy, double d)
|
|
|
|
{
|
2010-12-08 10:26:42 +02:00
|
|
|
double cr = d/2;
|
|
|
|
double tr = d0/2;
|
2010-12-08 10:52:58 +02:00
|
|
|
double s = arc2angle(MAX_STEP, cr);
|
2010-12-08 10:26:42 +02:00
|
|
|
double a;
|
|
|
|
|
2010-12-08 02:14:49 +02:00
|
|
|
assert(mill);
|
2010-12-08 10:26:42 +02:00
|
|
|
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);
|
2010-12-08 02:14:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void drill(double x, double y)
|
|
|
|
{
|
2010-12-08 10:26:42 +02:00
|
|
|
printf("%f %f %f\n\n", x, y, -depth);
|
2010-12-08 02:14:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void do_cmd(char cmd, double v, int nl)
|
|
|
|
{
|
|
|
|
static int active = 0;
|
|
|
|
static int metric = 1;
|
|
|
|
static int slotting = 0;
|
2010-12-08 10:26:42 +02:00
|
|
|
static double x = 0, y = 0, x0 = 0, d = 0;
|
2010-12-08 02:14:49 +02:00
|
|
|
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];
|
2010-12-08 10:26:42 +02:00
|
|
|
fprintf(stderr, "T%d -> %f\n", n, d);
|
2010-12-08 02:14:49 +02:00
|
|
|
if (mill)
|
2010-12-08 10:26:42 +02:00
|
|
|
active = d >= d0;
|
2010-12-08 02:14:49 +02:00
|
|
|
else
|
|
|
|
active = d >= d0 && d <= d1;
|
2010-12-08 10:26:42 +02:00
|
|
|
fprintf(stderr, "%g %g %g -> %d\n", d, d0, d1, active);
|
2010-12-08 02:14:49 +02:00
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
x = metric ? v : IN2MM(v);
|
|
|
|
break;
|
|
|
|
case 'Y':
|
|
|
|
if (!active) {
|
|
|
|
slotting = 0;
|
|
|
|
break;
|
|
|
|
}
|
2010-12-08 10:26:42 +02:00
|
|
|
if (!metric)
|
|
|
|
v = IN2MM(v);
|
2010-12-08 02:14:49 +02:00
|
|
|
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;
|
|
|
|
}
|
2010-12-08 10:26:42 +02:00
|
|
|
y = v;
|
2010-12-08 02:14:49 +02:00
|
|
|
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;
|
2010-12-08 10:26:42 +02:00
|
|
|
mill = 1;
|
2010-12-08 02:14:49 +02:00
|
|
|
break;
|
|
|
|
case 3:
|
2010-12-08 10:26:42 +02:00
|
|
|
mill = 0;
|
2010-12-08 02:14:49 +02:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage(*argv);
|
|
|
|
}
|
|
|
|
|
|
|
|
header();
|
|
|
|
body();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|