View
2
Download
0
Category
Preview:
Citation preview
Schematic Drawing Program by K7QO
Version 0.80
September 22, 2007
by
Chuck Adams, K7QO
k7qo@commspeed.net
CONTENTS
Preface .............................................................. iii
1. Introduction ...................................................... 1
2. Basic Pen Movement in Plotters ................................. 7
2.1 HOME ............................................................... 8
2.2 MOVE ............................................................... 8
2.3 DRAW ............................................................... 9
2.4 hpgl2ps .......................................................... 13
2.5 resistor ......................................................... 15
2.6 ground ............................................................ 29
2.7 wires ............................................................. 34
3. Advanced Labeling ............................................... 66
3.1 Component Labeling Completed 75
4. Components ....................................................... 79
4.1 capacitors ....................................................... 79
4.2 K8IQY PVXO Circuit .............................................. 82
4.3 Labels at Nodes ................................................. 86
4.4 Diodes and Zeners ............................................... 89
4.5 Comment Line in Data Set ....................................... 92
5. Node Checking .................................................... 98
6. regulator ....................................................... 102
7. JFETs ............................................................ 105
8. inductors ....................................................... 112
9. crystals ........................................................ 115
10. DPDT switch .................................................... 118
11. varactor diode ................................................ 123
ii
12. variable resistor ............................................. 125
13. goto node ...................................................... 128
14. transformer .................................................... 136
15. transistor ..................................................... 140
16. Uses for Labeling Relative to Nodes ........................ 142
iii
LIST OF FIGURES
Figure Page
1. Grapics grid. .................................................... 1
2. Resistor symbols. ............................................... 7
3. Final fixed resistor symbol. ................................... 8
iv
Preface
One can only question the sanity of any one who attempts or wishes
to do a programming project from scratch. But this project is aimed
at the avid QRPer and electronics experimenter.
I can tell you why I am a QRPer and why I love to build and design
things from scratch, whether it be electronics or programming. I grew
up so poor that the cockroaches went next door to eat. My brothers
and I and the rest of the kids our age in Wink TX built all our own
toys because Santa Claus skipped us. Well, almost.
We all saved our nickels and dimes for mail order from Lafayette
Electronics. We bought Popular Electronics and we scrounged old TVs
and radios for parts. We learned at an early age to avoid the 600V
points in tube circuits.
I even went dumpster diving as a kid for envelopes at the school
to collect stamps. Still have the collection. Went to the bank with
a dollar every other day to get rolls of pennies to search through for
rare coins. I have a dozen 1909S VDB pennies in very fine condition.
No. You can’t bid on them on ebay. Sorry.
My philosophy is that it is better to teach a man to fish than to
give him a fish. And that is why this tutorial on how to write a schematic
drawing program from scratch. It teaches graphics and some programming
techniques and just maybe some one can teach me a thing or two. I do
not claim to know it all. But I have written a schematic program that
seems to get the attention of people and they ask me what program I’m
using and I tell them one that I wrote. It isn’t unlike getting on
the air with a homebrew transceiver and everyone expects you to be using
some commercial equipment. They are surprised when you tell them that
you built it.
So, I’m showing all this from scratch and you’ll see my writing progress
as we go along. Be patient and do the exercises and you will be all
the better for it. You will see why I did what I did and why there
are not many free programs out there that do this. And I never gave
away my program ’cuz I knew that I’d some day have to explain everything
otherwise I’ll be so busy just answering stupid questions. This way
I don’t have to do anything. Ha.
v
Good luck and keep in touch. I’m interested in your ideas and contributions
and I will give credit where credit is due.
Chuck, K7QO
Prescott, AZ
P.S. This is most likely an example of the design document that you
would like with every Open Source program known to man. But I can tell
you that few programmers have the patience and the time to do this.
In fact, I bet (no disrespect intended) that programmers don’t like
writing and just want to create works of art in code. I’ve seen many
that can’t spell and don’t know the difference between to, too, two
and tu (Latin). Doing any document for public consumption is subject
to harsh criticism by those that haven’t written code or don’t like
the program that you wind up with. And there are many people, who by
the way irritate the hell outta me, that just love to pick out someone
and then publically verbally abuse them because of personality clashes.
The Internet is truely a room full of monkeys. (smiley goes here).
Some people have too much time on their hands. Fortunately, I am not
one of them. At least, I hope so.
vi
1. Introduction
Here is what you will need for this project. You won’t need a computer
today, but you will later on. I use the Linux Operating System because
it has all the tools I’ll ever need and it is all free. My favorite
is the kubuntu 7.04 version of Linux. Google for pointers to it online.
It seems to be the number one version at present time. Let’s not get
into flame wars over distributions (distros) of Linux.
I do all my programming in C, so get a book if you don’t already
have one and start at the beginning and go until the end. Just go to
the library or used book store and any introduction to C book that you
can find. Here is a partial list from my personal library.
Teach Yourself C in 24 Hours by Tony Zhang, SAMS Publishing, ISBN 0-672-31068-6
Beginning C by Ivor Horton, Wrox Press Ltd, ISBN 1-861001-142
First, we’ll spend a little time here talking about schematic symbols.
You will need some grid graphics paper and a pencil and a ruler. I
use Mead 4 Quadrille Graph Paper that has 4 squares per inch on one
side and 5 squares per inch on the back side.
Figure 1. Graphics grid.
1
Now for this project I went to my bookcase and pulled out the 2004
edition of the ARRL Handbook. On page vii is a page of schematic symbols.
We’ll use those as starters and later we’ll go to other books and online
looking for schematic symbols and we may even just go and make up some
of our own as needed. You’ll see later on. Been there. Done that.
Look at the simple resistor. You’ll see it has 3 vertices on each
side of the symbol. If you glance through the book itself at random
schematics you’ll see that each instance of a resistor uses the same
symbol and the same number of vertices. But the length of the lines
into and out of the resistor vary depending upon the geometry of the
schematic. This I used in defining my input data for components for
the program.
Take the graph paper and reproduce the resistor symbol. Here are
some samples:
a
b
c
Figure 2. Resistor symbols.
Which symbol do you like best? My guess is c. But it’s still not
quite right, so we’ll make one more attempt at it.
2
Figure 3. Final fixed resistor symbol.
OK. That looks a lot better. The reason for putting all the vertices
on intersecting grid lines is that mechanical plotters like the old
Calcomp drum plotter and later in the 80’s for microcomputers the old
Amdek DXY-100 flatbed plotter mechanically could only place the pen
at descrete points on the surface. These plotters use stepping motors
for pen movements. This meant that the pen moved only to and from points
equally spaced in the vertical and the horizontal directions. So when
I first wrote my program I had to use pen drawing commands that went
to a point on the paper that was an intersection to one of these grid
points. The distance between vertical and horizontal lines depended
upon the resolution of the stepping motors and the gears.
What we’ll do is assume that we are still using the old equipment,
but we’ll be using the inkjet and laser printers of the twentyfirst-century
to do the real work.
3
0PIN #1
1
2
3
4
5
6
7
8 9PIN #2
a bb b
b
b
b
b
b
b
b b
Figure 4. Vertex coordinates for fixed resistor.
In Figure 4 a dot has been placed at each vertex and labeled with
a number in red. In drawing the resistor we will start the pen (emulating
the pen in a physical plotter) at vertex 0. With the pen down, i.e.
touching the paper, we will move the pen to 1, then 2, and each additional
point until we get to the end point 9. This will result in a symbol
for the resistor drawn only with uniform lines. We have to write a
computer program to create the sequence of plotting commands to generate
the symbol.
First we need the starting coordinates (x,y) for point 0. Let’s
just call that point the ’current’ pen position. We then move a certain
distance in the horizontal direction to point 1. Let’s look at the
relative coordinates of each vertex from point 1 for the zig-zag pattern
that makes up the resistor symbol. I’ll show the vertex number and
the relative coordinates (x,y) below. You can get the numbers by counting
the grid points from point 1 to each point as shown in the table:
1 - (a+0,0) 3 - (a+3,-3) 5 - (a+7,-3) 7 - (a+11,-3)
2 - (a+1,3) 4 - (a+5,3) 6 - (a+9,3) 8 - (a+12,0)
4
I want you to remember these numbers. They will appear in the computer
program we will generate later. The line segments a and b that connect
the resistor symbol to the rest of the schematic will vary in length
depending upon the geometry. So here is how I determined how to input
data into my program, since the data consisted of an ASCII file. I
am not using a graphics interface. To put a resistor into a diagram
I input a data line that gives me the length from point 0 to point 9
in integer units, a scale factor for the resistor symbol, an angle in
integer degrees at which the symbol is to be drawn, and two labels.
Here is what the line looks like
r1 pin number length scale angle info1 info2
and an example may look like
r1 1 100 4 0 R1 100K
You can see that the fields of information are separated by one or
more spaces. The letter r means that this information is for a resistor,
the 1 means it is a fixed resistor as we’ll see later on we need a variable
resistor and we’ll call it type 2, the pin number connected to the node,
the distance from vertex 0 to 9 is 100 units, the scale factor is 4,
the angle is 0 (in this case the resistor on the page will be horizontal,
and two labels will be drawn with the resistor symbol (the R1 and 100K).
The pin number concept may seem strange at this time, but for parts
that have more than two pins and for parts that are not symmetrical
this becomes important.
Since the distance from vertex 0 to vertex 9 is 100 units, it is
fairly easy to calculate the lengths of the end lines labeled a and
b. We’ll make them equal lengths in all cases to simplify programming
issues. The vertical distance from vertex 1 to vertex 8 is 12*scale.
We get this from the node coordinates multiplied by the scale factor.
The scale factor does not multiply the segment a and b lengths. So
if the resistor symbol eats up 12*scale distance between 0 and 9, then
the remaining distance left is length-12*scale. In our example here
we have length=100, scale=4, and we get length-12*scale=100-12*4=100-48=52.
Since a=b and a+b=52, then a=b=26.
You can see that you are restricted to a minimum distance or length
value of 48 if you have a scale of 4 for the resistor, otherwise you’ll
have no end leads for the resistor in your resulting schematic.
5
Let me run my program and put several resistors in a row with different
scale factors and labeling.
Figure 5. Resistors.
6
Here are the four lines of data to get the four resistors. The tilde
symbols are used to indicate that no labels are used for that component.
I’ll explain later why you need or should want this option in drawing
more complex schematics.
r1 1 100 4 0 R1 100K
r1 1 100 3 0 ∼ ∼
r1 1 100 1 0 ∼ ∼
r1 1 100 1 45 ∼ ∼
Note the scale factors of 4, 3, and 1 for the three resistors. The
figure shown above is magnified due to the software to create this document,
so when you get to doing this the figure will be smaller. Note that
the top resistor is at an angle of 45 degrees. I put the ability to
draw at any angle just in case it is needed, but for most schematics
you will only need 0, 90, 180, and 270 degree angles to cover horizontal
and vertical orientation of components in most schematics.
Typically in a single schematic you wouldn’t be changing the scale
factor for the resistors except in special cases. Look around in the
literature and see if any one did change symbol sizes in the same schematic.
It may occur in some commercial packages.
7
2. Basic Pen Movement in Plotters
Plotters have input commands that determine the next process in pen
motion and application to the plotting surface. There are two major
sets of commands that I know and have used. The HPGL for Hewlett-Packard
Graphics Language that was used for HP plotters. A current check of
the HP web page shows no plotters and there is the possibility that
HP no longer manufacturers plotters and concentrates on HP printers.
A number of other sites have used HP plotters for sale.
The other set of plotting commands are used for the Amdek old plotters
such as the Amdek DXY-100 and it is those commands that I am familiar
with and use. Now you don’t have to worry about buying a plotter. In
the Linux world there is a package that contains a way to convert HPGL
and Amdek commands to PostScript. The one that I’ll be using the dxy2ps
command.
http://ftp.math.purdue.edu/mirrors/ctan.org/graphics/hpgl2ps/
The above site has all the files in the directory along with the
manuals. This may require some work and experience with Linux to get
dxy2ps up and running. When time permits I’ll write something up for
the newbies. I found out that there is a bug in the above module, so
here is a tar file (tape archival) that you can get and create the program.
I have instructions later on for doing this.
http://www.commspeed.net/k7qo/hpgl2ps.tar
I also found the original Amdek X-Y Plotter DXY-100 Operation Manual
and I will scan it in and put it on my web page for a demonstration
of how we used to do things in the good old days. It will also show
you why the grid system worked and still works even today for graphics.
The Amdek plotter had a step size resolution of 0.1mm so think of
the grid distances between adjacent horizontal and vertical points as
0.1mm. The plotter could do an area 360mm by 260mm restricted by the
maximum movement allowed along the x and y axes respectively. That
amounts to 3600 steps horizontally and 2600 steps vertically. We will
not be restricted by that as we can use the DXY to PS conversion utility
to scale things to fit the page size used in our printers.
8
2.1 HOME
The HOME plot command lifts the plotting pen and moves the pen to
the ‘home’ position, which is x=0 and y=0. This corresponds to the
lower left hand of the page we are plotting on. It is assumed that
when the program starts up to plot that the current pen position is
(0,0) in the absolute coordinate system of the plotter. The HOME command
consists of the letter H being sent to the plotter via the parallel
port or USB port on more modern printers. In our case, since we are
not physically using a plotter, we will write the letter H to a file.
So here is a function in C that we will call when we want to send the
virtual pen to the home position:
void home() /* send pen to home position (0,0) */
{
fprintf(plotter,"H\n");
}
2.2 MOVE
The MOVE plot command lifts the plotting pen and moves the pen to
an absolute position (x1,y1) on the plotting surface. An example of
this would be M650,800 which would move the pen from its current position
to the point 650 steps to the right of the origin (0,0) and 800 steps
up from the origin. So programs must create (x1,y1) points determined
by grapics needs. The "move" means that the plotter pen leaves no line(s)
drawn on the plotting surface and can be moved to any position within
the 3600 by 2600 plotting area on the surface of the real physical device.
void movepen(int x,int y) /* move pen to (x,y) */
{
fprintf(plotter,"M%d,%d\n",x+xoffset, y+yoffset );
}
I named the C-function movepen just to make it obvious what the function
does. You will note that I added something to the (x,y) destination
9
point. I added an xoffset and yoffset. We will think of all our plotting
in the coordinate system corresponding to the plotting surface. But
I have the xoffset and yoffset that I read in at the start of the plotting
program to offset the entire page, if needed, for other programs or
printers that may for some odd reason not have the plotting centered
where I want them. It is just a simple change of the two values and
rerun the plot and I’m good to go.
2.3 — DRAW
The DRAW command for the plotter is D with destination (x,y). This
command causes the pen to be lowered, if it happens to be in the up
position, and the pen moved to the destination point (x,y). This results
in a line being drawn from the current position to the point given as
arguments to the command. I call my function line in order to denote
that any call to the command causes a line to be drawn from the current
pen position to the destination point.
void line(int x,int y) /* draw line from current pen position to (x,y) */
{
fprintf(plotter,"D%d,%d\n",x+xoffset,y+yoffset);
}
Now we have enough to draw any diagram we want. We just have to
put the filler in front to get it. In the Amdek manual there are some
sample programs written in, of all things, BASIC. I’ll just write them
in C for demo purposes to show a couple of things. We write and run
the C program, then use dxy2ps to generate a PostScript file, and from
there generate a PDF file. I won’t leave anything out, I promise. "Not
failure but low aim is crime." --- Alexander Pope.
In this example I will read in a data file that has whatever data
we need for the offset and the parameters for the plot. Then the program
will generate a file called plotfile that contains the Amdek plot commands
to create the figure. This file we will use to create the final plot.
So here goes.
10
The program will look like this and let’s put it in a file named
squares.c:
#include <stdio.h>
void exit(int); /* function prototype for exit */
void home(); /* function prototype for home */
void movepen(int,int); /* function prototype for movepen */
void line(int,int); /* function prototype for line */
/* file pointers for input file and plotter output */
FILE *fp_input, *plotter;
int xoffset, yoffset; /* plot area offset */
main(int argc,char *argv[])
{
int i, length;
/* open up input filename for reading */
fp_input = fopen(argv[1],"r");
/* if input file does not exist scrap the whole project */
if( fp_input== NULL )
{
printf(" input file %s does not exist \n",argv[1]);
exit(0);
}
/* all plotter commands to be written into plotfile */
plotter = fopen("plotfile","w");
home(); /* make sure pen is in home position at start of program */
/* read in xoffset and yoffset from input data file */
fscanf(fp_input,"%d%d",&xoffset,&yoffset);
11
/* let’s draw 15 squares centered about the point (300,300) */
length=30; /* set starting length for side of square */
for( i=0 ; i<15 ; i++ ) /* lets do 15 squares */
{ movepen(300-length/2,300-length/2); /* move pen to lower left of square*/
line(300+length/2,300-length/2); /* line from lower left to lower right */
line(300+length/2,300+length/2); /* line from lower right to upper right */
line(300-length/2,300+length/2); /* line from upper right to upper left */
line(300-length/2,300-length/2); /* line from upper left to lower left */
length += 20; /* increase side length by 20 units */
}
}
void home() /* move pen to home (0,0) position */
{
fprintf(plotter,"H\n");
}
void movepen(int x,int y) /* move pen to (x,y) */
{
fprintf(plotter,"M%d,%d\n",x+xoffset, y+yoffset );
}
void line(int x,int y) /* draw line from current pen position to (x,y) */
{
fprintf(plotter,"D%d,%d\n",x+xoffset,y+yoffset );
}
I want you to type in the above program into a file squares.c. Now
don’t just blindly type it in, think about what each statement does.
It is a good review of your C programming and it will double check your
typing. Now compile the program under Linux with the following command.
gcc -o squares squares.c -lm
12
If everything works correctly, then you will have an executable called
squares. The -lm option in the compilation is needed later when we
need the <math.h> header file for the sine, cosine, and other math functions
and we need the linker/loader to get the libraries that contain the
functions. We now need a file squares.data and in it put the single
line
200 200
Now run the binary program with the input data by typing in the following
at the command prompt in Linux:
./squares squares.data
Hopefully you won’t see anything happen. If you do an ls you will
see a new file plotfile. If you look at the contents, it should have
something like:
H
M500,500
M485,485
D515,485
D515,515
D485,515
D485,485
M475,475
D525,475
D525,525
D475,525
D475,475
M465,465
...
The first line is to put the pen in the HOME position. The next
line, M500,500, moves the pen to (500,500) which is (300,300) plus the
xoffset=200 and yoffset=200. We then move to (485,485) which is 30
to the left and 30 down from the (500,500) position. We then draw four
13
lines that make up the first square with vertices (485,485), (515,485),
(515,515), (485,515).
Check the math to see if that is correct. Then we move to (475,475)
which is the lower left corner of the next square and we draw it. Do
this until done. That’s how easy it is. Now the plotfile doesn’t do
us any good as it is not graphics yet. So let me help you convert this
to something you can see and print.
2.4 — hpgl2ps
In order to convert the Amdek pen commands in the file plotfile we
need utility called dxy2ps. I gave a reference to a web page earlier.
Turns out there is a bug in one of the routines. I have fixed the package
(at least it seems well behaved) and we can test it. Do the following:
cd
mkdir dxy2ps
cd dxy2ps
wget http://www.commspeed.net/k7qo/hpgl2ps.tar
tar xvf hpgl2ps.tar
make
sudo cp dxy2ps hpgl2ps /usr/local/bin
This will put dxy2ps into your Linux systems /usr/local/bin directory
so that you can access the utility from other locations in the system.
Now move into the directory where you have the plotfile created from
the plot routine earlier. Now run
dxy2ps plotfile > test.ps
evince test.ps
The use of evince or gv or other programs for viewing PostScript
files will do. I just happen to like and use evince for either PostScript
or PDF files. You should see the multiple squares embedded within each
other as shown below. This now means we are now ready to go back to
generating the schematic program.
14
15
2.5 — fixed resistor
Let’s write the routine to draw a fixed resistor. First of all let
me say that when I draw a component I have the pen located where one
end of the component is to be placed in the schematic. For now let’s
assume that the pen is located at (x,y). We wish to draw a resistor.
The data input has the form of
r1 pin length scale angle info1 info2 color
where we will use the component and type indicator r1 to invoke or call
the function resistor. One critical piece of information is the angle.
It determines if the resistor is to be drawn to the right, upward, to
the left, or downward from the current (x,y) point. So I have written
a rotation function to rotate a component about the (x,y) point. Here
is the rotate function.
rotate(int *x,int *y,int rot)
{
int xp,yp,cos1,sin1;
double conversion=3.14159265358979/180.0;
xp=(*x)*cos((double)rot*conversion)-(*y)*sin((double)rot*conversion);
yp=(*x)*sin((double)rot*conversion)+(*y)*cos((double)rot*conversion);
*x=xp;
*y=yp;
}
By the way. The rotation is in a positive direction if it is counter-clockwise
to the page about the pivot point. I’ll demostrate shortly.
16
OK. Let’s write a function that will draw a resistor at point (x,y)
when called.
void resistor(int xs,int ys,int pin,int length,int scale,int rot)
{ /* pin number is not used for a fixed resistor */
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength;
halflength=(length-12*scale)/2;
movepen(xs,ys);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
x=xs+x1;y=ys+y1;movepen(x,y);
}
17
OK. We are now ready to rock-n-roll and start doing schematics. Won’t
be too interesting yet as we only have the resistor component. But
it will demonstrate some basics of doing schematics and what we are
yet to have to put into the program.
We are going to need a couple of things for our program. We will
use a single character to determine what component is next to be drawn
and sometimes a single character as a command. This gives us 52 possibilities
for single characters a-z and A-Z. Hopefully this will get it done.
Let me start out with the following commands and components implemented:
r ---- resistor
e ---- end of input data
G ---- go to point (x,y)
Here is our first test data set and I’ll put it into a file pgm001.dat
just for grins. This will allow us to put several resistors on a page
and illustrate the rotation and placement capabilities.
200 200
G 0 0
r1 1 100 4 90 R1 100K
G 50 0
r1 1 200 4 90 R2 50K
G 100 0
r1 1 200 6 90 R3 4.7K
G 200 0
r1 1 100 4 180 R4 100
G 200 0
r1 1 100 4 270 R5 470
e
18
Here is the program and it will be called pgm001.c so that it will
not be confused with later programs.
/* pgm001.c create non-labeled fixed resistors */
/* chuck adams, k7qo, september 2007 */
#include <stdio.h>
#include <math.h>
void exit(int); /* function prototype for exit */
void home(); /* function prototype for home */
void movepen(int,int); /* function prototype for movepen */
void line(int,int); /* function prototype for line */
void resistor(int,int,int,int,int,int);
void rotate(int *,int *,int);
/*****************************************************/
/* global variables */
/*****************************************************/
FILE *fp_input, *plotter; /* file pointers */
int x, y; /* current pen position */
int xoffset, yoffset; /* plot area offset */
int scale, rot; /* scale factor and angle */
char info1[80], info2[80]; /* labels for components */
/*****************************************************/
main(int argc,char *argv[])
{
int i, length;
int pin, type;
char ch;
/* open input filename for reading */
fp_input = fopen(argv[1],"r");
/* if input file does not exist scrap the whole project */
19
if( fp_input== NULL )
{
printf(" input file %s does not exist \n",argv[1]);
exit(0);
}
/* all plotter commands to be written into plotfile */
plotter = fopen("plotfile","w");
home(); /* pen in home position at start of program */
/* read in xoffset and yoffset from input data file */
fscanf(fp_input,"%d%d",&xoffset,&yoffset);
/* current pen position */
x=0; y=0;
/**************************************************************/
/* begin reading in of schematic data */
/* r length scale angle info1 info2 color */
/* e for end of input */
/* G for go to (x,y) */
/**************************************************************/
/* input until EOF or e character in input stream */
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’G’: /* go direct to point (x,y) */
{
fscanf(fp_input,"%d%d",&x,&y);
movepen(x,y);
break;
}
case ’r’: /* resistor */
{
20
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) resistor(x,y,pin,length,scale,rot);
break;
}
} /* end of switch */
} /* end of while */
}
void home() /* move pen to home (0,0) position */
{
fprintf(plotter,"H\n");
}
void movepen(int x,int y) /* move pen to (x,y) */
{
fprintf(plotter,"M%d,%d\n",x+xoffset, y+yoffset );
}
void line(int x,int y) /* draw line from current pen position to (x,y) */
{
fprintf(plotter,"D%d,%d\n",x+xoffset,y+yoffset );
}
void resistor(int xs,int ys,int pin,int length,int scale,int rot)
{
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength;
halflength=(length-12*scale)/2;
movepen(xs,ys);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
21
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
x=xs+y1;y=ys+y1;movepen(x,y);
}
void rotate(int *x,int *y,int rot)
{
int xp,yp,cos1,sin1;
double conversion;
conversion=3.14159265358979/180.0;
xp=(*x)*cos((double)rot*conversion)-(*y)*sin((double)rot*conversion);
yp=(*x)*sin((double)rot*conversion)+(*y)*cos((double)rot*conversion);
*x=xp;
*y=yp;
}
and here is the output after we did the following sequence:
gcc -o pgm001 pgm001.c -lm
./pgm001 pgm001.dat
dxy2ps plotfile > pgm001.ps
evince pgm001.ps
22
As you can see, without labeling it is very difficult to see exactly
what is going on. The resistors, from left to right are R1, R2, R3,
R4, and R5. We have a tough job ahead in getting labeling done, but
we will get to it. First let’s look at the program as it stands now.
A. Sets up input from a filename given in the command line.
B. Sets up a file named plotfile to output the Amdek plotting commands.
C. Reads in xoffset and yoffset first thing from the input file.
D. Begins reading in components to plot and other command info.
E. Quits when we hit the ’e’ for end of input or EOF if we forget it.
We don’t have color yet and of course a lot of desired components.
Now you and I do not want to specify part by part where they go. So
I came up with a scheme whereby we put the next part at the last point
of the last component drawn unless we alter the flow. This works out
very well.
So we need to add to the program a current pen position, which we
already have in (x,y). We just need to update it to move to the end
of the last component. So we add variable xlast,ylast and we make them
global. At the end of each component drawing function we will set xlast,ylast
to the correct values. Then upon return to the main function we update
x,y to these new values. Now if we don’t put a G or goto a point between
components then the two parts are assumed to be connected. Then you
don’t have to have the coordinate points known specifically.
Let’s go ahead and add color at this time. I’ll add a function that
will read in the r,g,b color coordinates and then write them to the
plotfile with a pen change command for dxy2ps. The pen change normally
23
takes a value of 1-9 that gets converted internally to a line width
determined from an array of values. I modified the dxy2ps routine to
check for a special value of 10 and output a PostScript command to set
the color. To get back to another color you will have to issue another
color command C.
void getcolor()
{
float red,green,blue;
fscanf(fp_input,"%f %f %f",&red,&green,&blue);
fprintf(printer,"J1 %8.3f %8.3f %8.3f\n",red,green,blue);
}
Here is the modified program, with the changes highlighted in red.
#include <stdio.h>
#include <math.h>
void exit(int); /* function prototype for exit */
void home(); /* function prototype for home */
void movepen(int,int); /* function prototype for movepen */
void line(int,int); /* function prototype for line */
void resistor(int,int,int,int,int,int);
void rotate(int *,int *,int);
void getcolor(); /* function prototype for getcolor */
/*****************************************************/
/* global variables */
/*****************************************************/
FILE *fp_input, *plotter; /* file pointers */
int x, y; /* current pen position */
int xoffset, yoffset; /* plot area offset */
int scale, rot; /* scale factor and angle */
char info1[80], info2[80]; /* labels for components */
24
/*****************************************************/
main(int argc,char *argv[])
{
int i, length;
int pin, type;
char ch;
/* open input filename for reading */
fp_input = fopen(argv[1],"r");
/* if input file does not exist scrap the whole project */
if( fp_input== NULL )
{
printf(" input file %s does not exist \n",argv[1]);
exit(0);
}
/* all plotter commands to be written into plotfile */
plotter = fopen("plotfile","w");
home(); /* pen in home position at start of program */
/* read in xoffset and yoffset from input data file */
fscanf(fp_input,"%d%d",&xoffset,&yoffset);
/* current pen position */
x=0; y=0;
/**************************************************************/
/* begin reading in of schematic data */
/* r length scale angle info1 info2 color */
/* e for end of input */
/* G for go to (x,y) */
/**************************************************************/
/* input until EOF or e character in input stream */
25
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’G’: /* go direct to point (x,y) */
{
fscanf(fp_input,"%d%d",&x,&y);
movepen(x,y);
break;
}
case ’r’: /* resistor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) resistor(x,y,pin,length,scale,rot);
break;
}
case ’C’: /* change pen color */
{
getcolor();
break;
}
} /* end of switch */
} /* end of while */
}
void home() /* move pen to home (0,0) position */
{
fprintf(plotter,"H\n");
}
void movepen(int x,int y) /* move pen to (x,y) */
26
{
fprintf(plotter,"M%d,%d\n",x+xoffset, y+yoffset );
}
void line(int x,int y) /* draw line from current pen position to (x,y) */
{
fprintf(plotter,"D%d,%d\n",x+xoffset,y+yoffset );
}
void resistor(int xs,int ys,int pin,int length,int scale,int rot)
{
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength;
halflength=(length-12*scale)/2;
movepen(xs,ys);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
x=xs+y1;y=xs+y1;movepen(x,y);
}
27
void rotate(int *x,int *y,int rot)
{
int xp,yp,cos1,sin1;
double conversion;
conversion=3.14159265358979/180.0;
xp=(*x)*cos((double)rot*conversion)-(*y)*sin((double)rot*conversion);
yp=(*x)*sin((double)rot*conversion)+(*y)*cos((double)rot*conversion);
*x=xp;
*y=yp;
}
void getcolor()
{
float red,green,blue;
fscanf(fp_input,"%f %f %f",&red,&green,&blue);
fprintf(plotter,"J10 %8.3f %8.3f %8.3f\n",red,green,blue);
}
Now in creating all these examples, let us hope that I don’t mess
up and overwrite the previous material. I include files in the creation
of this document, so please watch me carefully for any mess that occurs.
Here is a new input file called plot002. Note the addition of the
color parameters at the end of each resistor line. R1 is black, R2
is red, R3 is green, R4 is blue and R5 is yellow (red and green combined).
200 200
G 0 0
r1 1 100 4 90 R1 100K
G 50 0
C 1.0 0.0 0.0
r1 1 200 4 90 R2 50K
G 100 0
C 0.0 1.0 0.0
r1 1 200 6 90 R3 4.7K
28
G 200 0
C 0.0 0.0 1.0
r1 1 100 4 180 R4 100
G 200 0
C 1.0 1.0 0.0
r1 1 100 4 270 R5 470
e
Here is the output.
29
You will note that yellow is a terrible color to use for plotting
schematics. Let’s check the serial plotting of components if we remove
the G commands. Here is plot003 data file:
200 200
r1 1 100 4 90 R1 100K
C 1.0 0.0 0.0
r1 1 200 4 90 R2 50K
C 0.0 1.0 0.0
r1 1 200 6 90 R3 4.7K
C 0.0 0.0 1.0
r1 1 100 4 180 R4 100
C 1.0 1.0 0.0
r1 1 100 4 270 R5 470
e
and here is the resulting plot.
30
2.6 — ground
I realize this is not going as fast as you probably want, but I want
to detail everything as I go along. It turns out that this process
allows me to add things to the program that I had left out previously
because there were options that I wasn’t interested in. But I figure
in this venue there are some things that others might want and since
I can program them in at this time, I might as well do it.
Now go back to the Handbook and look at the schematic symbols page.
There are three ground symbols. Let’s see if we can go back to the
graph paper and create them.
b
b b
b
For the chassis ground I think I’ll go with the upper left symbol.
You can, of course, make up your own and use it. Now because there
are three ground symbols (in my original program I had only the chassis
ground and had never needed the other two) we need to identify them
differently. If we use g as the control character in the input data
file to identify a ground symbol, then we can easily add a number immediately
following it to determine the type of ground. So we’ll use g1 for the
chassis ground, g2 for the earth ground and g3 for the digital/analog
ground symbol. The dot shown in the graph is the zero reference point
or the point to which a component will be connected. You will note
that I did put a vertical line at this point as the compononent will
31
provide that part in the diagram.
I now have a programming option. I can write three separate functions,
one for each ground symbol or do one function and pass the type as an
argument. I’ll start first with separate functions in order to just
get things going faster and simplify the programming.
So, without further ado, here are three functions g1, g2 and g3 for
the respective ground symbols.
void g1(int x, int y, int scale, int rot)
{
/* ground symbol vertices */
/* 1 - (-3,0)
2 - (3,0)
3 - (-4,-2)
4 - (-1,-2)
5 - (2,-2)
*/
/* draw horizontal line at top of symbol */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 1 to 3 */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=-4*scale; y1=-2*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex (0,0) to 4 */
x1=0*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=-1*scale; y1=-2*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 2 to 5 */
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=2*scale; y1=-2*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
32
void g2(int scale, int rot)
{
int x1,y1;
/* earth ground vertices */
/* 1=(-4,0) 2=(4,0) first horizontal line
3=(-3,-1) 4=(3,-1) second horizontal line
5=(-2,-2) 6=(2,-2) third horizontal line
7=(-1,-3) 8=(1,-3) third horizontal line
*/
/* draw horizontal line at top of symbol */
x1=-4*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=4*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 3 to 4 */
x1=-3*scale; y1=-1*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=-1*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 5 to 6 */
x1=-2*scale; y1=-2*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=2*scale; y1=-2*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 7 to 8 */
x1=-1*scale; y1=-3*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=1*scale; y1=-3*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
void g3(int x, int y, int scale, int rot)
{
int x1,y1;
/* ground symbol vertices */
/* 1=(-3,0) 2=(3,0) 3=(0,-4) */
33
/* draw horizontal line at top of symbol */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 1 to 2 */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=0*scale; y1=-4*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 2 to 3 */
x1=0*scale; y1=-4*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
We are now ready to test the routine. First add the call to g1,
g2, and g3 in the switch statement for the input. I made mine to look
like:
case ’g’: /* ground symbol */
{
fscanf(fp_input,"%d%d%d",&type,&scale,&rot);
if( type==1 ) g1(scale,rot);
if( type==2 ) g2(scale,rot);
if( type==3 ) g3(scale,rot);
break;
}
34
Then I made up a test data set for the g1 test that consisted of:
200 200
g1 4 0 r1 1 100 4 90 R1 100K
G 50 0 g1 3 0 r1 1 100 4 90 R2 10K
G 100 0 g1 2 0 r1 1 100 4 90 R3 10K
G 150 0 g3 4 0 r1 1 100 4 90 R4 5K
G 200 0 g2 4 0 r1 1 100 4 90 R4 5K
G 250 0 g2 3 0 r1 1 100 4 90 R4 5K
e
And I got for output:
You will note the change in size of the chassis ground system with
the different scale factor. You pick the size that you think looks
most pleasing and go with it. And one more thing. You do not have
to have each component on a separate line. I find this feature useful
in doing lengthy and complicated schematics, such as the Small Wonder
Labs SWXX+ series.
Now feel free to jump in at any point and send me email about anything
that you think could be done better. This is my way of doing it and
it is all done without previous help from anyone at any time. You don’t
find this stuff published anywhere as people who do this do it for money
and they sell you a program for a lot of money. Now there is a GPL
based system but I find the GUI almost impossible to use and I can draw
much faster using a text file for input.
35
2.7 — wires
In order to connect components in a schematic we will need lines
for wires or PC board traces/interconnects. This is a simple routine.
We just draw a line from the current position to a point where one end
of the next component is to go. In order to do this I only need the
length, an angle, and a color. So here is the switch part that goes
into the main function and the function to draw the wire.
case ’w’: /* a wire */
{
fscanf(fp_input,"%d%d",&length,&rot);
wire(length,rot);
break;
}
void wire(int length,int rot)
{
int x1,y1;
x1=length; y1=0; rotate(&x1,&y1,rot); line(x+x1,y+y1);
x=x+x1; y=y+y1; /* move pen to end of wire */
}
In order to test this I created the following input file.
200 200
g1 4 0
r1 1 200 4 90 R1 100
w 100 0
r1 1 200 4 270 R2 100
g1 4 0
e
36
And for this I got
OK. I am going to stop here. I want you to go through
what is here at this point. I want you to create the program
and get it working up to this point.
For the last example I want you to look at the data file
and go through it line by line and make sure you understand
what each component is and why you get the figure I show.
Does your program do the same thing?
Before I go one step further I need 10 people to email
me that they are doing this. I don’t want to be the only
one doing this. And when you email me, answer the following
questions:
HOMEWORK:
1. What issue of the ARRL HB do you have? Need the most
common one for obtaining some schematics to draw.
2. Go out on the web and find 3 schematics for ham radio
stuff. 1. Pretty simple (say 20-30 components). 2. Next
level up (say 50 or so components). 3. Fairly complicated
(>70 components).
We can then recreate these in phases to show how to do
these things at any level. And I want something that we
can share without too much confusion about which one we are
using.
3. Using the graph paper, layout an alphabet using fixed
points on the grid. What size would you use to get the caps,
lower case, and numbers and some special symbols that you
might want to use in labels, etc.?
37
Here is the list of commands currently working. Remember we only
have 52 letters to work with before we have to start getting creative.
Make a list of what you think you’ll need in the future. Look at schematics
and see what symbols you will need. BTW we won’t be doing tubes. I
don’t own a single tube and I don’t want to own any, thank you very
much. I don’t have any high voltage transformers, etc. and no room
for same.
let function format
--- --------- ---------------------------------
C - set pen color C r g b
G - go to point G x1 y1
e - end of input e
g - ground gN scale angle color
N=1 chassis ground
N=2 earth ground
N=3 digital/analog ground
r - resistor rN pin length scale angle info1 info2
N=1 fixed resistor
w - wire w length angle color
38
The following pages show what I used for the individual characters.
I won’t bore you with the individual vertices for each character. The
origin for each character is the lower left point of the grid. This
is the most time consuming task of the whole project and also the most
important.
39
40
41
Now comes the lower case letters. Here you can be flexible and be
creative at the same time. You do not have to copy me and if you have
a better idea, I’d be more than happy to listen. All this I did without
instruction or help from the outside world, so it may not be the best
way.
42
43
OK. We now have a character set. I’ll show you how to use it. First
of all, we often will need to put text in our schematic at some place.
So I use a ’t’ on input to denote that I want to put a string of text
into the schematic. I denote an (x,y) point where to start the text,
a size specification to allow flexibility and an angle. So in the input
switch section I added the following switch:
case ’t’: /* text from (x,y) with size and angle */
{
fscanf(fp_input,"%d%d%d%d",&x,&y,&szinfo,&alpha);
fscanf(fp_input,"%c",&ch); /* eat up lf at end of input line */
readinfo();
print(x,y,alpha,szinfo,info);
break;
}
Because of the way input is done in C, I added an additional read
to consume the line feed character. Make sure you do not put any additional
blanks, etc after the last parameter. This places the input pointer
at the first position of the line after the "command line" setting the
program up to print text on the schematic. Here is an example two lines
to get text on the schematic.
t 200 200 4 0
K7QO EXAMPLE TEXT
The first line shows we are going to do text which starts at (200,200)
on the page with a scaling of 4 at an angle of zero degrees. The text
will be the string "K7QO EXAMPLE TEXT".
The function readinfo() inputs the characters on the line and places
them into the array info and terminates the string with a NULL. We then
call the function print that will start at point (x,y) and draw (print)
the equivalent characters onto the schematic page. I’ll show some examples
after the routine print is developed.
44
Here is the function readinfo that will input the text string for
the entire line into the array info[]. Be sure to declare this array
in the global section and not in the main function. I did char info[100]
as I doubt that you will ever need more than 100 characters in a single
string.
readinfo()
{
int i=0;
/* read in characters into info[] until lf found and terminate with NULL */
fscanf(fp_input,"%c",&info[i]);
while( info[i] != 0x0a )
{
i++;
fscanf(fp_input,"%c",&info[i]);
}
info[i] = (int) NULL;
}
Because I needed a slightly different rotate routine for printing,
I have another rotatel (rotate letters) function that inputs an expression
and then places the results into a variable pointed to by the corresponding
argument.
void rotatel(int alpha,int x,int y,int *xp,int *yp)
{
double pi2 = 3.14159265/180.0;
*xp = x*cos(alpha*pi2) - y*sin(alpha*pi2);
*yp = x*sin(alpha*pi2) + y*cos(alpha*pi2);
}
45
For the function print, which is by far the largest section of code
for the entire program, here is a sample showing only the letter A section.
I’ll use this for discussion so as not to clutter the page and our minds.
Note that sz is used instead of scale in order to save a LOT of typing
in doing all the letters and symbols. Trust me, you don’t want to use
size as the variable name here...
void print(int x,int y,int alpha,int sz,char letters[])
{
int i;
int horizontal; /* current pen position */
int xp,yp;
movepen(x,y);
horizontal=0; /* distance from (x,y) start of string */
/* do for loop for number of characters in info */
for( i=0 ; i<strlen(letters) ; i++)
{
switch(letters[i])
{
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
/* add case statements for each character and symbol here */
} /* end of switch statement */
} /* end of for loop */
} /* end of print function */
46
Here is the grid for the letter A. I have marked the vertices with
numbers so that you can see the points and how they correspond to the
proper segment of code.
1 b
2 b
3b
4b
5b
6b
7 b 8b
9b
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
Some points about the code. Note that we use a movepen in going
from 6 to 7. If you don’t, then you will get a nasty looking line drawn.
Same for 8 to 9. Point 9 is the point of origin for the next character,
if there are any more to do on the page.
To add this to your code that you have done so far:
1. Add variable declaration for char info[100] in global section.
Add #include <string.h> for access to strlen funtion.
2. Add "t" switch in main routine.
3. Add functions for readinfo, rotatel (letter l at end) and print
to source code.
4. Compile and make sure everything is correct.
5. Run the test data set shown on next page and see if you get the
same results. Then add one character at a time to the print routine
and test. This makes debugging the program easier IMHO.
47
Here is the test input file:
200 200
g1 4 0
r1 1 200 4 90 R1 100
w 100 0
r1 1 200 4 270 R2 100
g1 4 0
t 0 0 3 0 0
AAA
C 1.0 0.0 0.0
t 220 100 5 0
A
t 100 300 8 90
A
e
And here is the resulting plot:
48
OK. Hopefully you have done everything up to this point. Probably,
because of my writing style and programming style you may have missed
something important. It’s not your fault as this is a difficult project.
So, here is my listing at this point to compare with yours. Let
me know where I left something out in the text. I’ll wait for you to
get all your characters defined in the print function. At the end of
this program listing I will show a sample test set and the output for
all the characters at this point. FYI.
49
#include <stdio.h>
#include <math.h>
#include <string.h>
/* schematic drawing program de K7QO, Chuck Adams */
/* pgm003.c */
/* you can get this exact pgm via */
/* wget http://www.commspeed.net/k7qo/pgm003.c */
/* wget http://www.commspeed.net/k7qo/pmg003.dat */
/* gcc -o pgm003 pgm003.c -lm for compilation */
/* function prototype definitions */
void exit(int);
void home();
void movepen(int,int);
void line(int,int);
void getcolor();
void resistor(int,int,int,int,int,int);
void rotate(int *,int *,int);
void rotatel(int,int,int,int *,int *);
void g1(int,int);
void g2(int,int);
void g3(int,int);
void wire(int,int);
void print(int,int,int,int,char letters[]);
/*****************************************************/
/* global variables */
/*****************************************************/
/* */
FILE *fp_input, *plotter; /* file pointers */
int xoffset, yoffset; /* plot area offset */
int scale, rot; /* scale factor and angle */
int x, y; /* current pen position */
char info[100];
char info1[80],info2[80]; /* labels for components */
/*****************************************************/
50
main(int argc,char *argv[])
{
int i,length;
int type;
int szinfo, alpha;
int pin;
char ch;
/* open input filename for reading */
fp_input = fopen(argv[1],"r");
/* if input file does not exist scrap the whole project */
if( fp_input== NULL )
{
printf(" input file %s does not exist \n",argv[1]);
exit(0);
}
/* all plotter commands to be written into plotfile */
plotter = fopen("plotfile","w");
home(); /* place pen in home position at start */
/* read in xoffset and yoffset from input data file */
fscanf(fp_input,"%d%d",&xoffset,&yoffset);
/* set pen position to home position */
x=0; y=0;
/**************************************************************/
/* begin reading in of schematic data */
/* resistor === r length scale angle info1 info2 color */
/* end === e for end of input */
/* goto x,y === G for go to (x,y) */
/* set color == C r g b */
/**************************************************************/
51
/* input commands until EOF or e character in input stream */
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’G’: /* go direct to point (x,y) */
{
fscanf(fp_input,"%d%d",&x,&y);
movepen(x,y);
break;
}
case ’C’: /* set pen color */
{
getcolor();
break;
}
case ’g’: /* ground symbol */
{
fscanf(fp_input,"%d%d%d",&type,&scale,&rot);
if( type==1 ) g1(scale,rot);
if( type==2 ) g2(scale,rot);
if( type==3 ) g3(scale,rot);
break;
}
case ’r’: /* resistor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) resistor(x,y,pin,length,scale,rot);
break;
}
case ’w’: /* a wire */
{
fscanf(fp_input,"%d%d",&length,&rot);
wire(length,rot);
break;
52
}
case ’t’: /* text from (x,y) with size and angle */
{
fscanf(fp_input,"%d%d%d%d",&x,&y,&szinfo,&alpha);
fscanf(fp_input,"%c",&ch); /* eat up lf at end of input line */
readinfo();
print(x,y,alpha,szinfo,info);
break;
}
} /* end of switch */
} /* end of while */
} /* end of main */
void home() /* move pen to home (0,0) position */
{
fprintf(plotter,"H\n");
}
void movepen(int x,int y) /* move pen to (x,y) */
{
fprintf(plotter,"M%d,%d\n",x+xoffset,y+yoffset);
}
void line(int x,int y) /* draw line from current pen position to (x,y) */
{
fprintf(plotter,"D%d,%d\n",x+xoffset,y+yoffset );
}
void resistor(int xs,int ys,int pin,int length,int scale,int rot)
{
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength;
halflength=(length-12*scale)/2;
53
movepen(xs,ys);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
x=xs+x1;y=ys+y1; movepen(x,y);
}
void rotate(int *x,int *y,int rot)
{
int xp,yp,cos1,sin1;
double conversion;
conversion=3.14159265358979/180.0;
xp=(*x)*cos((double)rot*conversion)-(*y)*sin((double)rot*conversion);
yp=(*x)*sin((double)rot*conversion)+(*y)*cos((double)rot*conversion);
*x=xp;
*y=yp;
}
void getcolor()
{
float red,green,blue;
fscanf(fp_input,"%f %f %f",&red,&green,&blue);
fprintf(plotter,"J10 %8.3f %8.3f %8.3f\n",red,green,blue);
54
}
void g1(int scale, int rot)
{
int x1,y1;
/* ground symbol vertices */
/* 1 - (-3,0)
2 - (3,0)
3 - (-4,-2)
4 - (-1,-2)
5 - (2,-2)
*/
/* draw horizontal line at top of symbol */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot);
movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* now draw line from vertex 1 to 3 */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot);
movepen(x+x1,y+y1);
x1=-4*scale; y1=-2*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* now draw line from vertex (0,0) to 4 */
x1=0*scale; y1=0*scale; rotate(&x1,&y1,rot);
movepen(x+x1,y+y1);
x1=-1*scale; y1=-2*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* now draw line from vertex 2 to 5 */
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot);
movepen(x+x1,y+y1);
x1=2*scale; y1=-2*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
55
void g2(int scale, int rot)
{
int x1,y1;
/* earth ground vertices */
/* 1=(-4,0) 2=(4,0) first horizontal line
3=(-3,-1) 4=(3,-1) second horizontal line
5=(-2,-2) 6=(2,-2) third horizontal line
7=(-1,-3) 8=(1,-3) third horizontal line
*/
/* draw horizontal line at top of symbol */
x1=-4*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=4*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 3 to 4 */
x1=-3*scale; y1=-1*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=-1*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 5 to 6 */
x1=-2*scale; y1=-2*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=2*scale; y1=-2*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 7 to 8 */
x1=-1*scale; y1=-3*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=1*scale; y1=-3*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
void g3(int scale, int rot)
{
int x1,y1;
/* ground symbol vertices */
/* 1 - (-3,0)
2 - (3,0)
56
3 - (0,-4)
*/
/* draw horizontal line at top of symbol */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 1 to 2 */
x1=-3*scale; y1=0*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=0*scale; y1=-4*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
/* now draw line from vertex 2 to 3 */
x1=0*scale; y1=-4*scale; rotate(&x1,&y1,rot); movepen(x+x1,y+y1);
x1=3*scale; y1=0*scale; rotate(&x1,&y1,rot); line(x+x1,y+y1);
movepen(x,y); /* move back to starting point */
}
void wire(int length,int rot)
{
int x1,y1;
x1=length; y1=0; rotate(&x1,&y1,rot); line(x+x1,y+y1);
x+=x1; y+=y1; movepen(x,y); /* move pen to end of wire */
}
readinfo()
{
int i=0;
/* read in characters into info[] until lf found and terminate with NULL */
fscanf(fp_input,"%c",&info[i]);
while( info[i] != 0x0a )
{
i++;
fscanf(fp_input,"%c",&info[i]);
}
info[i] = (int) NULL;
}
void print(int x,int y,int alpha,int sz,char letters[])
57
{
int i;
int horizontal; /* current pen position */
int xp,yp;
movepen(x,y);
horizontal=0; /* distance from (x,y) start of string */
/* do for loop for number of characters in info */
for( i=0 ; i<strlen(letters) ; i++)
{
switch(letters[i])
{
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
/* add case statements for each character and symbol here */
} /* end of switch statement */
} /* end of for loop */
} /* end of print function */
void rotatel(int alpha,int x,int y,int *xp,int *yp)
{
double pi2 = 3.14159265/180.0;
*xp = x*cos(alpha*pi2) - y*sin(alpha*pi2);
*yp = x*sin(alpha*pi2) + y*cos(alpha*pi2);
}
58
let function format
--- --------- ---------------------------------
C - pen color C r g b
G - go to point G x1 y1
e - end of input e
g - ground gN scale angle
N=1 chassis ground
N=2 earth ground
N=3 digital/analog ground
r - resistor r length scale angle info1 info2
t - text t x1 y1 scale angle
TEXT ..............
w - wire w length angle
59
I do not have any idea how much experience people have with UNIX
and Linux. I personally go way back to the good old days, but I love
now and wouldn’t change it for just about anything.
I use the vi or now the vim editor. Use vimtutor from a command
line in a console window to learn it. Then browse the web for anything
else you can find on tutorials. Let me show you how easy or how the
job of inserting all the other letters, numerals, and additional critters
into the switch statement in the routine print.
Start with the letter A that I have already done. I make a copy
of the following in a file called template. Then I read it into the
file by placing the cursor on the line before the position to make another
copy and I do :r template and the editor reads in the file into my current
position. Here is an example showing the code in print and after I
have inserted two copies of the template into the file.
Before I read in two copies of template
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
Inside the call to rotatel, the coordinates of each vertex is modified
and returned in xp and yp. I have highlighted the first three calls
and the (x,y) values for the coordinates 2, 3 and 4.
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
60
Here is what the contiguous text in the source file looks like after
I have added two copies of template:
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
61
I know it looks fairly complicated, but it’s not. OK, on the next
page I highlight characters to be changed to add B and C.
62
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz; break;
case ’A’:
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz; break;
On the next page I show the grid layout for A, B and C. I’m afraid
you have to do the same thing for all the other 50+ characters that
you want to define.
63
1 b
2 b
3b
4b
5b
6b
7 b 8b
9b 1 b
2 b3b
4b
5b6b7 b
8b
9b
10
b 11b
4 b
3b
2b
1b
5 b
6
b
7
b8b
9b
A 1=(0,0) 2=(0,6) 3=(1,7) 4=(3,7) 5=(4,6)
6=(4,0) 7=(0,3) 8=(4,3) 9=(6,0)
B 1=(0,0) 2=(0,7) 3=(3,7) 4=(4,6) 5=(4,5)
6=(3,4) 7=(0,4) 8=(4,3) 9=(4,1) 10=(3,0) 11=(6,0)
C 1=(4,6) 2=(3,7) 3=(1,7) 4=(0,6) 5=(0,1)
6=(1,0) 7=(3,0) 8=(4,1) 9=(6,0)
case ’A’:
/* 2 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+4*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,3*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
case ’B’:
/* 2 */ rotatel(alpha,horizontal+0*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+4*sz,5*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+3*sz,4*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+0*sz,4*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+3*sz,4*sz,&xp,&yp);line(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,3*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+4*sz,1*sz,&xp,&yp);line(x+xp,y+yp);
/* 10 */ rotatel(alpha,horizontal+3*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 1 */ rotatel(alpha,horizontal+0*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 11 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 11 */ horizontal += 6*sz;
64
break;
case ’C’:
/* 1 */ rotatel(alpha,horizontal+4*sz,6*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 2 */ rotatel(alpha,horizontal+3*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 3 */ rotatel(alpha,horizontal+1*sz,7*sz,&xp,&yp);line(x+xp,y+yp);
/* 4 */ rotatel(alpha,horizontal+0*sz,6*sz,&xp,&yp);line(x+xp,y+yp);
/* 5 */ rotatel(alpha,horizontal+0*sz,1*sz,&xp,&yp);line(x+xp,y+yp);
/* 6 */ rotatel(alpha,horizontal+1*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 7 */ rotatel(alpha,horizontal+3*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
/* 8 */ rotatel(alpha,horizontal+4*sz,1*sz,&xp,&yp);line(x+xp,y+yp);
/* 9 */ rotatel(alpha,horizontal+6*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
/* 9 */ horizontal += 6*sz;
break;
Please note that for every letter where point 1 is not the lower
right point (0,0) then you must add a movepen to get to that point at
the start of each letter. I don’t do it for A or B because the first
point in drawing the letter is the lower left corner. The current pen
position (x,y) will be at that point already. It won’t affect the time
to generate a plot much anyway and the computer is so fast we don’t
care.
I recommend you print off the pages with the characters and generate
a table of vertices for each symbol and letter and then add one at a
time to the code. Then run a test program that uses the character to
double check. It takes a little time, but it is time worth spending.
You may run into a situation where the character just doesn’t suit you,
so feel free to change it. Thanks.
Watch the spacing on the lower case characters and the special symbols.
You can reduce the spacing in the last two lines that move the pen and
adjust the horizontal position.
65
Here is a table of the vertices for the letters and symbols. I’ll
try to catch errors as I will use these to input the code to generate
each letter.
The order pairs of numbers will be arranged from vertex 1, 2, 3,
... to N, where N may be large. The points may not be in the order
drawn in some cases and some points are used more than once with a movepen
command. Go back to pages 39 through 43 and check my work before you
begin modifying your program.
A (0,0) (0,6) (1,7) (3,7) (4,6) (4,0) (0,3) (4,3)
B (0,0) (0,7) (3,7) (4,6) (4,5) (3,4) (4,3) (4,1) (3,0)
C (4,6) (3,7) (1,7) (0,6) (0,1) (1,0) (3,0) (4,1)
D (0,0) (0,7) (3,7) (4,6) (4,1) (3,0)
E (0,0) (0,7) (4,7) (0,3) (3,3) (4,0)
F (0,0) (0,7) (4,7) (0,3) (3,3)
G (4,6) (3,7) (1,7) (0,6) (0,1) (1,0) (3,0) (4,1) (4,0) (4,3) (3,3)
H (0,0) (0,7) (0,3) (4,3) (4,0) (4,7)
I (0,0) (2,0) (1,0) (1,7) (0,7) (2,7)
J (0,2) (0,1) (1,0) (3,0) (4,1) (4,7)
K (0,0) (0,7) (0,3) (4,7) (1,4) (4,0)
L (0,0) (0,7) (4,0)
M (0,0) (0,7) (2,4) (4,7) (4,0)
N (0,0) (0,7) (4,0) (4,7)
O (1,0) (3,0) (4,1) (4,6) (3,7) (2,7) (0,6) (0,1)
P (0,0) (0,7) (3,7) (4,6) (4,4) (3,3) (0,3)
Q (0,1) (0,6) (1,7) (3,7) (4,6) (4,1) (3,0) (1,0) (2,2) (4,0)
R (0,0) (0,7) (3,7) (4,6) (4,4) (3,3) (0,3) (2,3) (4,0)
S (0,1) (1,0) (3,0) (4,1) (4,3) (3,4) (1,4) (0,5) (0,6) (1,7) (3,7) (4,6)
T (2,0) (2,7) (0,7) (4,7)
U (0,7) (0,1) (1,0) (3,0) (4,1) (4,7)
V (0,7) (2,0) (4,7)
W (0,7) (1,0) (2,3) (3,0) (4,7)
X (0,0) (4,7) (0,7) (4,0)
Y (0,7) (2,3) (2,0) (4,7)
Z (0,7) (4,7) (0,0) (4,7)
66
Since I am having you do this program and at the same time I am writing
this tutorial, I am too writing the program from scratch instead of
using the old program of mine. In this way I can make improvements
and modifications as I go along. I would recommend that you add a couple
of letters at a time, compile the program and run it with a test data
set shown below. Here is my results after just doing the capital letters
A through Z.
Here is the input data set to my new program that I called pgm004.c.
100 100
t 0 300 6 0
ABCDEFGHIJKLMNOPQRSTUVWXYZ
e
and here is the output.
Note that I have made some cosmetic changes from what I originally
did on pages 39-43. The letter M has the center vertex lowered to (2,2).
I felt this more pleasing to the eye and I redid V and W and placed
a small horizontal line in Z as the Europeans would write the letter
zed.
I told you that doing the lettering would be the most difficult part
of the program. My program pgm003.c was 354 lines long. pgm004.c is
now 653 lines long and we haven’t even gotten to the lower case letters
and numerals and special symbols. Here goes and I will copy pgm004.c
to pgm005.c and add the lower case letters and symbols and demonstrate
the results.
Now, with pgm005.c at 1010 lines long, I have added the numerals
and special symbols. Here is the graphics output. Note that I have
added a back quote symbol, added a tick mark to the letter O so that
it would be different from the numeral zero.
67
With the work on lower case letters, pgm006.c is now 1351 lines long.
And here are the results. I’ve made some small modifications to a couple
of the letters. You’ll be able to tell and when you add your letters
you can experiment. One of the nice things about computers today is
that we don’t consume anything but a small amount of electrical energy
in doing this work.
Here is the data set that produced the above:
100 100
t 0 300 6 0
ABCDEFGHIJKLMNOPQRSTUVWXYZ
t 0 220 6 0
1234567890
t 0 140 6 0
.,:;/%’‘[]?+-=#()*<>
t 0 60 6 0
abcdefghijklmnopqrstuvwxyz
t 0 -20 6 0
Nicely Done With Spacing
e
68
100 100
C 1.0 0.0 0.0
t 400 400 3 0
RED TEXT AT 0 DEGREES
C 0.0 1.0 0.0
t 400 450 3 45
GREEN TEXT AT 45 DEGREES
C 0.0 0.0 1.0
t 370 450 3 90
BLUE TEXT AT 90 DEGREES
C 0.0 1.0 1.0
t 350 430 3 135
TEXT AT 135 DEGREES
C 0.0 0.0 0.0
t 360 400 3 270
BLACK TEXT AT 270 DEGREES
e
69
3 — Advanced Labeling
Now that we have a print routine for generating text strings and
putting them into our schematic, it is time to add labeling to our components.
Take a look at a complicated shematic diagram. Let’s use the NorCal
Sierra from the 2004 ARRL Handbook, page 17.84, so please turn in your
hymnal to page 17.84.
Look at the resistors. Resistors that are horizontal usually have
the Rn label above and the component value below. But not always and
that is what makes writing a program difficult. You don’t know ahead
of time which way it should be done, so I chose to leave an option open
to do it by hand. I am trying to minimize the number of fields that
I have to input for each component so that the input doesn’t get too
complex. It is bad enough as it is.
Resistors that are vertical in the schmatic diagram usually have
the label and the value on the right hand side of the component. We
will assume that is the case and I’ll show you how I chose to abort
that option if I wanted.
In the input for a resistor there is an INFO1 and INFO2 field for
the label (INFO1) and the component value (INFO2). For a resistor placed
at an angle of 0 degrees I will place info1 above the resistor and info2
below the resistor. It would be nice to have the text centered between
the ends of the resistor. Since the resistor is of length length, we
can take the space occupied by the text, say number of characters times
6*scale, and subtract that from the length and then divide by two to
figure out the distance from the end point to start the text. We will
place the text above the resistor by 6 units since the vertices of the
points on the resistor are 3 units from the horizontal line.
70
So now, the code in the function resistor is now:
void resistor(int x,int y,int length,int scale,int rot)
{
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength,text_spacing;
halflength=(length-12*scale)/2;
movepen(x,y);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(x+x1,y+y1);
xlast=x+x1; ylast=y+y1;
if( rot==0 )
{ text_spacing = 6*strlen(info1)*scale;
x1 = x + (length-text_spacing)/2;
y1 = y + 3*(scale+2); /* info1 goes above the line */
print(x1,y1,0,1*scale-1,info1);
y1 = y - 3*scale - 7*scale; /* info2 goes below the line */
print(x1,y1,0,1*scale-1,info2);
}
}
71
Here is a test file:
200 200
g1 4 0 w 100 90
r1 1 200 4 0 R1 100K
e
And here is the output from a run using the current program state.
Oh. I have completed the lettering in print.
You will note that the R1 isn’t quite right. That is because in
the calculation for the distance occupied by the string info1 I assumed
that every character in the string occupies 6 units horizontally. That
isn’t the case for the numeral 1. So we have to add code to feed info1
to a function and return the true space. Let’s save that until later.
OK? I think we need more practice in writing this program to work on
fine details later. BTW (by the way). If you draw resistors at 90,
180, and 270 degrees you won’t have any labeling. We have to write
that section of code.
In my previous version of the schematic drawing program that I have
been using for decades I had the code built into each component to do
the labeling. Now I see that is probably a bad idea. I can save a
lot of typing by calling a single function to do the labeling. How
would you do this? I’m going to go off here for a day or two and design
this. It shouldn’t be too difficult. You get this code working. I
probably left out some details. I moved info1 and info2 into the global
section and I’ll try to go back in this document and make the adjustments
to make the code match. I told you this was difficult. Doing it and
then explaining it takes a lot of care, planning, and time and energy.
72
OK. I went off to the paper and pen and figured out the scheme to
correctly get the character strength length that it will take up on
a plot. Now the routine char len is not fancy. It is straight forward
brute force code. Nothing elegant at all, but it does the job, is easy
to change and it is obvious what it is doing.
Add to the global variable section just before the main function
the variable text scale and read this value into the program at the same
time you read xoffset and yoffset.
fscanf(fp_input,"%d%d%d",&xoffset,&yoffset,&text_scale);
While I was at it I added something else. One problem in inputting
strings of characters is that things get terminated with a space or
tab character. So, if you want a space in a component label we have
to put some character from the keyboard that we will never use in a
component label. I chose the tilde, ∼, character. This comes in handy
when doing components that you want one of the labels to be blank or
none at all. Just a ∼ in the field will result in a blank for the label.
Or a ∼ in a string will result in a space in the string. I’ll demonstrate.
Here is the result of the corrected program code and on the next
page the routines added to the program to get this done.
73
void resistor(int xs,int ys,int length,int scale,int rot)
{
/* resistor vertices */
/* 1 - (0,0) 2 - (1,3) 3 - (3,-3) */
/* 4 - (5,3) 5 - (7,-3) 6 - (9,3) */
/* 7 - (11,-3) 8 - (12,0) */
int x1,y1,halflength,text_spacing;
halflength=(length-12*scale)/2;
movepen(xs,ys);
/* a */ x1=halflength+0*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 2 */ x1=halflength+1*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 3 */ x1=halflength+3*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 4 */ x1=halflength+5*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 5 */ x1=halflength+7*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 6 */ x1=halflength+9*scale; y1=3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 7 */ x1=halflength+11*scale; y1=-3*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* 8 */ x1=halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
/* b */ x1=2*halflength+12*scale; y1=0*scale; rotate(&x1,&y1,rot);
line(xs+x1,ys+y1);
x=xs+y1;y=ys+y1;movepen(x,y);
label(x,y,length,scale,rot,info1,info2);
}
void label(int x,int y,int length,int scale,int angle,char info1[],char info2[])
{
int text_spacing,x1,y1;
if( rot==0 )
{ text_spacing = char_length(info1);
x1 = x + (length-text_spacing)/2;
y1 = y + 3*(scale+2);
print(x1,y1,0,text_scale,info1);
text_spacing = char_length(info2);
74
x1 = x+(length-text_spacing)/2;
y1 = y-6*scale-7*text_scale;
print(x1,y1,0,text_scale,info2);
}
}
int char_length(char info1[])
{
int i,len=0;
for( i=0; i<strlen(info1); i++ )
{
switch(info1[i])
{
case ’A’: len+=6*text_scale; break;
case ’B’: len+=6*text_scale; break;
case ’C’: len+=6*text_scale; break;
case ’D’: len+=6*text_scale; break;
case ’E’: len+=6*text_scale; break;
case ’F’: len+=6*text_scale; break;
case ’G’: len+=6*text_scale; break;
case ’H’: len+=6*text_scale; break;
case ’I’: len+=6*text_scale; break;
case ’J’: len+=6*text_scale; break;
case ’K’: len+=6*text_scale; break;
case ’L’: len+=6*text_scale; break;
case ’M’: len+=6*text_scale; break;
case ’N’: len+=6*text_scale; break;
case ’O’: len+=6*text_scale; break;
case ’P’: len+=6*text_scale; break;
case ’Q’: len+=6*text_scale; break;
case ’R’: len+=6*text_scale; break;
case ’S’: len+=6*text_scale; break;
case ’T’: len+=6*text_scale; break;
case ’U’: len+=6*text_scale; break;
case ’V’: len+=6*text_scale; break;
case ’W’: len+=6*text_scale; break;
case ’X’: len+=6*text_scale; break;
case ’Y’: len+=6*text_scale; break;
case ’Z’: len+=6*text_scale; break;
case ’1’: len+=4*text_scale; break;
case ’0’: len+=6*text_scale; break;
75
case ’~’: len+=6*text_scale; break;
case ’ ’: len+=6*text_scale; break;
case ’*’: len+=6*text_scale; break;
}
}
return(len-2*text_scale);
} /* end of char_length */
OK, we are progressing fairly well through the hard stuff. After
this we can get back to adding more components and doing some schematics
that are non-trivial. Here is a file to test the labeling for horizontal
resistors right now to show that there is still some tuning to do.
200 200 3
g1 4 0 w 100 90
r1 1 200 4 0 R1 100K
G 0 100 w 100 90
r1 1 200 4 0 R2 47K
G 0 200 w 100 90
r1 1 200 4 0 R3~4.7K ~
r1 1 200 4 0 ~ R4~10
e
76
One of the problems with this diagram is that I personally like junction
markers where one lead from one component connects to another. So I
added an addition input command "x" for junctions and just made a little
filled square at the current (x,y) position. Here is the part of the
case switch in the main function and the new file to insert junction
marks and the resulting plot. A color change just for the junction
can be used to mark points that are under discussion for a presentation
or writing materials.
case ’x’: /* junction */
{
/* draw three small squares centered at the point (x,y) */
movepen(x-1,y-1);line(x+1,y-1);line(x+1,y+1);line(x-1,y+1);line(x-1,y-1);
movepen(x-2,y-2);line(x+2,y-2);line(x+2,y+2);line(x-2,y+2);line(x-2,y-2);
movepen(x-3,y-3);line(x+3,y-3);line(x+3,y+3);line(x-3,y+3);line(x-3,y-3);
/* added diamond to give almost a circular effect */
movepen(x,y-4);line(x+4,y);line(x,y+4);line(x-4,y);line(x,y-4);
movepen(x,y); /* move pen back to node */
break;
}
77
and the input file
200 200 3
g1 4 0 w 100 90
x
r1 1 200 4 0 R1 100K
G 0 100 w 100 90
x
r1 1 200 4 0 R2 47K
G 0 200 w 100 90
x
r1 1 200 4 0 R3~4.7K ~
C 1.0 0.0 0.0
x
C 0.0 0.0 0.0
r1 1 200 4 0 ~ R4~10 0
e
will result in the following more beautiful diagram IMHO. Also, one
of the things that I really like about PostScript and PDF is that when
you enlarge the diagrams you don’t lose clarity. Vector graphics is
the way to go. The illustrations shown in the tutorial are not drawn
to scale and in most cases are magnified for ease of use.
78
3.1 — Component Labeling Completed
I’ll now show you the rest of the label routine and if interested
I can come back and give you all the gory details, but you should take
the time to see just how I did the mathematics. It is interesting.
Here is the routine to add 180 degree component placement, which
means the component is horizontal to the left of the (x,y) position.
void label(int x,int y,int length,int scale,int angle,char info1[],char info2[])
{
int text_spacing,x1,y1;
if( rot==0 ) /* component to the right from (x,y) */
{ text_spacing = char_length(info1);
x1 = x + (length-text_spacing)/2;
y1 = y + 3*(scale+2);
print(x1,y1,0,text_scale,info1);
text_spacing = char_length(info2);
x1 = x+(length-text_spacing)/2;
y1 = y-6*scale-7*text_scale;
print(x1,y1,0,text_scale,info2);
movepen(x,y);
}
if( rot==180 ) /* component to the left from (x,y) */
{ text_spacing = char_length(info1);
x1 = x-length+(length-text_spacing)/2;
y1 = y + 3*(scale+2);
print(x1,y1,0,text_scale,info1);
text_spacing = char_length(info2);
x1 = x-length+(length-text_spacing)/2;
y1 = y-6*scale-7*text_scale;
print(x1,y1,0,text_scale,info2);
movepen(x,y);
}
}
79
200 200 3
g1 4 0 w 100 90 x
r1 1 200 4 180 R1 100K
G 0 100 w 100 90 x
r1 1 200 4 180 R2 47K
G 0 200 w 100 90 x
r1 1 200 4 180 R3~4.7K ~ x
r1 1 200 4 180 ~ R4~10
e
With the addition of
if( rot==90 ) /* component up from (x,y) */
{
x1 = x+5*scale; /* just to the right of the component */
y1 = y+length/2+4*text_scale; /* just above the 1/2 way */
print(x1,y1,0,text_scale,info1);
x1 = x+5*scale; /* just to the right of the component */
y1 = y+length/2-8*text_scale; /* just below the 1/2 way */
print(x1,y1,0,text_scale,info2);
}
80
200 200 3
g1 4 0
r1 1 200 4 90 R1 4.7K
x
r1 1 200 4 90 R2 47K
x
w 100 0 x
e
81
if( rot==270 ) /* component down from (x,y) */
{
x1 = x+5*scale; /* just to the right of the component */
y1 = y-length+length/2+4*text_scale; /* just above the 1/2 way */
print(x1,y1,0,text_scale,info1);
x1 = x+5*scale; /* just to the right of the component */
y1 = y-length+length/2-8*text_scale; /* just below the 1/2 way */
print(x1,y1,0,text_scale,info2);
}
will allow us to label the final drawings with components drawn downward
(270 degrees) from a node point or current (x,y).
200 200 3
g1 4 0
r1 1 200 4 90 R1 4.7K
x
r1 1 200 4 90 R2 47K
x
w 300 0 x
r1 1 200 4 270 R3 470
x
r1 1 200 4 270 R4 1M
g1 4 0
e
82
4 — Components
Now we can finally get to adding components that will aid in our
getting more complex schematics drawn. And the program is already at
1100+ lines of code with the large print function just about completed.
4.1 — Capacitors
We’ll start with the simplist of the capacitors and add the more
complicated ones as we get to them. The capacitor is represented by
two parallel lines separated by some distance. So here is my simple
cap without a curved line and represents a non-polarized cap such as
a mono.
83
And here is the snippet of code that generates a cap with specified
length between the end points. You have to figure the mid-point and
build the plates on each side and then draw the lines from the plates
to the end points.
case ’c’: /* capacitor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) capacitor(x,y,pin,length,scale,rot);
break;
}
void capacitor(int xs,int ys,int pin,int length,int scale,int rot)
{
int x1,y1,halflength,xcenter,ycenter;
halflength=length/2;
movepen(xs,ys);
x1=halflength-2*scale;y1=0; rotate(&x1,&y1,rot); line(xs+x1,ys+y1);
x1=halflength-2*scale;y1=-5*scale; rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
x1=halflength-2*scale;y1=+5*scale; rotate(&x1,&y1,rot); line(xs+x1,ys+y1);
x1=halflength+2*scale;y1=-5*scale; rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
x1=halflength+2*scale;y1=+5*scale; rotate(&x1,&y1,rot); line(xs+x1,ys+y1);
x1=halflength+2*scale;y1=0*scale; rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
x1=length;y1=0; rotate(&x1,&y1,rot); line(xs+x1,ys+y1);
x+=x1; y+=y1;
label(xs,ys,length,scale,rot,info1,info2);
movepen(x,y);
}
Here is the data file to produce the above plot.
84
200 200 3
g1 4 0
c1 1 200 4 90 C1 10p x
c1 1 200 4 90 C2 47p x
w 300 0 x
c1 1 200 4 270 C3 470p x
c1 1 200 4 270 C4 0.01
g1 4 0
e
For now it looks like the size is about right to match the resistors.
Time will tell. Now I know why I had the labeling typed in with each
component section. I could control where the labels were with respect
to each component. Maybe we may need to change the label routine to
feed an extra parameter that sets the minimum distance for closeness....
85
4.2 — K8IQY PVXO Circuit
OK. In order to determine the order of components as we need them
I will attempt to recreate Jim Kortge’s PVXO found at this web page.
I don’t know what schematic drawing routine he used, but let’s see what
we can do. Here is the URL:
http://www.k8iqy.com/testequipment/pvxo/pvxopage.htm
At the bottom of the page he has a link to his Atlanticon talk that
has a better schematic because it has the intersection marked for connections,
whereas the one on the web page above is missing the marks. This gives
us a chance to do a few things. Make up the schematic and build it
and debug it as we go along. I’ll build it Manhattan Style.
Let’s start at the power source and go until we need to build a component
not in our code at this time.
First think I am going to need is a small circle to indicate a connector
to the outside world. This is simple. I’ll have one edge of the circle
at the connection point. Since this can be an output or input I’ll
just use an ’o’ (lower case o) and I need the radius and the angle that
it takes off at the junction and the color. I’ll allow one label as
that is typically all we need.
o X1 Y1 radius angle info1 color
Here is the code for the main switch and the function to draw the
connector and the circle routine needed.
case ’o’: /* output/input connector */
{
int x1,y1; /* variable declaration may be moved */
fscanf(fp_input,"%d%d%s",&scale,&alpha,&info1);
x1=5*scale;y1=0;rotate(&x1,&y1,rot);circle(x+x1,y+y1,5*scale,72);
if( alpha==180 ) print(x-20,y+12,0,text_scale,info1);
movepen(x,y); /* move pen back to connecting point (x,y) */
break;
}
86
void circle(int xs,int ys,int radius,int nangle)
{
int x2,y2,i;
for( i=0 ; i<=nangle ; i++ )
{
x2=xs+cos( ((((double) i)/36.0))*3.1415926)*radius;
y2=ys+sin( ((((double) i)/36.0))*3.1415926)*radius;
if( i==0 ) /* check for first point and move to it */
{
x2=xs+cos( ((((double) 72)/36.0))*3.1415926)*radius;
y2=ys+sin( ((((double) 72)/36.0))*3.1415926)*radius;
movepen(x2,y2);
}
line(x2,y2); /* line to next point on the circumference */
}
} /* end of circle function */
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V w 40 0 x
w 30 0 x w 50 0
C 0.0 1.0 0.0 x
C 0.0 0.0 0.0
c1 1 100 3 270 C1 0.1^uF 0 g1 4 0
C 1.0 0.0 0.0
x
e
and with the above input file I get:
87
Just a quick note. I have added to the print routine a method to
generate some Greek symbols with room to add more if they become necessary.
The caret in front of the u causes a µ symbol to be placed in the next
character position. Here is the code in the function print that gets
it done. FYI.
case ’^’:
switch(letters[++i])
{
case ’u’:
rotatel(alpha,horizontal+0*sz,-3*sz,&xp,&yp);movepen(x+xp,y+yp);
rotatel(alpha,horizontal+0*sz,4*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+0*sz,1*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+1*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+2*sz,0*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+3*sz,1*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+3*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
rotatel(alpha,horizontal+3*sz,4*sz,&xp,&yp);line(x+xp,y+yp);
rotatel(alpha,horizontal+5*sz,0*sz,&xp,&yp);movepen(x+xp,y+yp);
horizontal += 5*sz;
break;
}
break; /* break for ^ */
We now have a problem. I have the pen currently located at the red
dot and I need to get back to the green dot to continue drawing the
schematic. Now I do not know the coordinates of the green dot without
having to do some serious calculating on moves in x and y directions
from the starting location.
88
So, what I did was add a counter for each node and a storage array
of the x- and y-coordinates at each node. Then all I have to do is
get to the node. One way is to ’yank’ or go back a number of nodes
with the y command followed by a number equal to the number of nodes
to back up. In the above I want to back up 1 node, so y 1 in the input
file will do the job.
So here is the added code to do this.
/* add this line into the global variables section */
int npoints=0,xpoint[300],ypoint[300];
/* add this code into the switch section in main */
case ’y’: /* back up # nodes */
{
fscanf(fp_input,"%d",&rot);
x=xpoint[npoints-rot];y=ypoint[npoints-rot];movepen(x,y);
break;
}
and add npoints++;xpoint[npoints]=x;ypoint[npoints]=y; at the last point
in every component that is drawn.
89
This will take some effort and some debugging to get implemented,
but I know you can do it. Need my help?
Here is the input with a ’yank’ to back up one node, from the red
dot to the green dot, and then draw the horizontal line to where the
next capacitor will be connected.
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V w 40 0 x
w 30 0 x w 50 0
C 0.0 1.0 0.0 x
C 0.0 0.0 0.0
c1 1 100 3 270 C1 0.1uF g1 4 0
C 1.0 0.0 0.0
x
C 0.0 0.0 0.0
y 1
w 80 0 0
e
90
Going back and removing the dot on the ground and going back to black
we continue on until we get to the regulator. Ooops. Time for some
for more code. Since we are using r for resistors we’ll try R for regulators.
Here is the data that will get everything up to the regulator.
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V w 40 0 x
w 30 0 x w 50 0 x c1 1 100 3 270 C1 0.1^uF g1 4 0
y 1
w 80 0 x
c1 1 100 3 270 C2 10^uF-16V g1 4 0 y1 w 80 0
e
4.3 — Labels at Nodes
One thing that I want to add to the code at this time. It is the
capability to add text/labels at points relative to a node. The reason
for this is that knowing where I am in the drawing by keeping track
of which component I last drew I know where the current node is. I
don’t have to know its number and I don’t need the (x,y) values either.
I can do a label with a relative offset delta-x and delta-y. So I’ll
use L for label followed by delta-x delta-y txt size angle text color
in the following format:
L dx dy txt_size angle text_for_label color
91
for the command line. The txt size gives me additional control to
override the default text size (note the different variable name) and
I can use color to emphasize or note something in the schematic.
So here is the code at this time added to the switch command in the
main function.
int txt_size,dx,dy;
case ’L’: /* label at (x+dx,y+dy) */
{
fscanf(fp_input,"%d%d%d%d%s",&dx,&dy,&txt_size,&alpha,&info1);
print(x+dx,y+dy,alpha,txt_size,info1);
movepen(x,y);
break;
}
And here is the data for the plot. I have set the two L (label)
commands apart from the rest of the text to emphasize what it looks
like. I use this a lot in schematic drawings. You’ll note that I added
a plus sign to the electrolytic capacitor using this command. We’ll
come back later to see if it is worth the effort to come up with a capacitor
type that automatically puts in the polarity for these types of caps.
92
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V w 40 0 x
C 1.0 0.0 0.0
L 10 10 1 0 FUSE
C 0.0 0.0 0.0
w 30 0 x w 50 0 x c1 1 100 3 270 C1 0.1uF g1 4 0
y 1
w 80 0 x
c1 1 100 3 270 C2 10uF-16V g1 4 0
C 1.0 0.0 0.0
L -15 60 2 0 +
C 0.0 0.0 0.0
y1 w 80 0
e
While I’m at it, let me show you an additional use for this new command.
Note that for C2 the value and the working voltage text takes up a lot
of room horizontally. Now I don’t want to modify the code to have 3
fields as this would cause a lot of typing for a complex circuit. So
how about I remove the -16V text and then use a L command to place it
below the value. In this manner:
At this point it will take you a little bit of practice and experience
to guess at the offset to put the text at the right position, but it
will be worth it once you get the hang of it. Also, you’re asking yourself
"how in the world am I going to get the whole schematic on a page?"
and the answer is that dxy2ps has a scale factor plus you can go from
portrait mode to landscape mode later, so don’t fret about it at this
time. Here is the data for the above:
93
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V w 40 0 x
L 10 10 1 0 FUSE
w 30 0 x w 50 0 x c1 1 100 3 270 C1 0.1^uF g1 4 0
y 1
w 80 0 x
c1 1 100 3 270 C2 10^uF g1 4 0
L -15 60 2 0 +
L 20 10 2 0 16V
y1 w 80 0
e
Ooops. A stroke of genius hit me or a bit of madness. Here is a
programming exam for you. The node is an intersection of two or more
components and or wires. It is not likely that I want a character at
dx==dy==0. So if dx==dy==0, then output the node number just to the
upper left of the intersection. That way you can use the information
to get back to the exact node with another command that I’ll show you
later.
I may even come up with a special command for debugging purposes
that puts a red dot at each node and the number. This makes sure the
code is working properly and all nodes are properly stored.......
Programming can be so much fun and you can get creative. FYI.
94
4.4 — Diodes and Zeners
Looking at what we have so far, I see that I missed the zener diode
on the input for reverse polarity protection. There are two ways to
protect and Jim picked one that involves a fuse and a diode. I choose
to use just a diode, so I will go and put a diode in place of the fuse.
I don’t like zener diodes in a circuit with reverse bias as it is a
noise generator. Look at any "white noise generator" circuit.
First we need a diode function and here are the points that I use.
Since filling an area isn’t an easy deal for starters, let’s just put
an open arrow and see how it looks. Here is what I get and the routine
to do it. Followed by the switch section to call the routine. Let’s
use d for diode as we haven’t used that letter yet.
b b
b
b
b
b
b
b
95
Here is the switch addition, the function addition to draw the diode,
and the resulting state of the K8IQY PVXO schematic. See how easy it
is to add components once we have the foundation laid out pretty well.
case ’d’: /* diode */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) diode(x,y,pin,length,scale,rot);
break;
}
void diode(int xd,int yd,int pin,int length,int scale,int rot)
{
int x1,y1,length2;
length2=length/2;
movepen(xd,yd);
/* draw from current position to connection on arrow */
x1=length2-3*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* draw the arrow in the next 4 lines */
x1=length2-3*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2-3*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2+3*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2-3*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* now draw line for cathode */
x1=length2+3*scale;y1=-4*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
x1=length2+3*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* move back to tip of arrow */
x1=length2+3*scale;y1=0*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
/* draw line from tip to next node */
x1=length;y1=0;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* call label routine to label the component */
label(xd,yd,length,scale,rot,info1,info2);
x+=x1;y+=y1;
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
}
96
If you go back to the ARRL HB, you’ll note there are various types
of diodes besides the rectifier diode shown above, so we can add the
type value after the d and the call the appropriate function for each
type of diode. So I’ll add it to the above listings and you can figure
out what I did as it is just like the ground symbols. We’ll make notes
as we add the types.
Now I don’t know what happens in other schematic drawing programs,
but in mine when I change a component the rest of the schematic shifts
to make room or take up the slack that may become available. This is
because of the drawing progressing from the last component and relative
to the rest of the circuit diagram. Looking at the above I can make
a couple of simple changes to the input and get a little bit cleaner
diagram and taking up a little less room. BTW, in the command dxy2ps
there is a command line option to scale your diagram, so if it is small
at the present time then try:
dxy2ps -s 3.5 plotfile >plot.ps
evince plot.ps
And most PDF viewers have a scale option also. Hope this helps.
A lot of these we learn as we use the software or read the manual.
97
200 200 2
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
d 140 3 0 D1 1N4001 x
c 100 3 270 C1 0.1uF g1 4 0
y1
w 90 0 x
c 100 3 270 C2 10uF g1 4 0
L 12 10 2 0 16V
L -15 60 2 0 +
y1 w 80 0
e
4.5 — Comment Lines in Data Set
Let me add at this point a simple mod to the program in the switch
statement in the main function:
case ’#’: /* input comment line */
{
readinfo();
break;
}
What this addition does is if the input contains a pound sign or
octothorpe then the pound sign and the rest of that line is read in
but not used for anything. This way you can add comments on the input
to aid in a few years if you happen to come back to the file and want
to know what it was/is/does.....
Here is an example applied to the work we’ve alread done. It makes
the input file larger, but it doesn’t affect the schematic drawn (unless
you put the # in the wrong place or in a place you want to remove something
for testing or whatever).
Trust me here and you can do this. Run your program with the modified
file here and show that you get the exact same diagram/schematic output.
98
200 200 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d 140 3 0 D1 1N4001 x
c 100 3 270 C1 0.1uF g1 4 0
# backup to top of C1
y1
w 90 0 x
c 100 3 270 C2 10uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity with C2
L -15 60 2 0 +
# backup to top of C2
y1 w 80 0
# end of input to drawing program
e
We could do some fancy esoteric programming for adding multiple diodes,
but I prefer to stay simple and make separate routines for each one
to reduce programming errors and make it easier to understand the code.
So here is my answer for the zener diode:
99
case ’d’: /* diode */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) diode(x,y,pin,length,scale,rot);
if( type==2 ) zener(x,y,pin,length,scale,rot);
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
break;
}
void zener(int xd,int yd,int pin,int length,int scale,int rot)
{
int x1,y1,length2;
length2=length/2;
movepen(x,y);
/* draw from current position to connection on arrow */
x1=length2-3*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* draw the arrow in the next 4 lines */
x1=length2-3*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2-3*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2+3*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2-3*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* now draw line for cathode */
x1=length2+3*scale;y1=-4*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
x1=length2+3*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* now draw lines at ends of cathode */
x1=length2+3*scale;y1=-4*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
x1=length2+5*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
x1=length2+3*scale;y1=4*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
x1=length2+1*scale;y1=6*scale;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* move back to tip of arrow */
x1=length2+3*scale;y1=0*scale;rotate(&x1,&y1,rot); movepen(xd+x1,yd+y1);
/* draw line from tip to next node */
x1=length;y1=0;rotate(&x1,&y1,rot); line(xd+x1,yd+y1);
/* call label routine to label the component */
label(xd,yd,length,scale,rot,info1,info2);
x+=x1;y+=y1;
}
100
Time for a sanity check. So far these are the commands I have implemented
as input into the schematic drawing program. I don’t know about you,
but when I do a "wc" for word count on my source program I get a count
of 1230 lines. Wow. That’s a lot of code so far.
let function format
--- --------- ---------------------------------
# - comment # any text to follow for documentation
C - pen color C r g b
G - go to point G x1 y1
L - label L dx dy txt_scale angle info1
c - capacitor cN pin length scale angle info1 info2
N=1 fixed capacitor
d - diode dN pin length scale angle info1 info2
N=1 rectifier diode
N=2 zener diode
e - end of input e
g - ground gN scale angle
N=1 chassis ground
N=2 earth ground
N=3 digital/analog ground
o - input/output o scale angle info1
r - resistor rN pin length scale angle info1 info2
N=1 fixed resistor
t - text t x1 y1 scale angle
TEXT ..............
w - wire w length angle
x - junction x
y - yank N nodes yN
N=number of nodes to back up
101
Here is the switch statement so you can compare with what you have
typed in so far:
/* input commands until EOF or e character in input stream */
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’G’: /* go direct to point (x,y) */
{
fscanf(fp_input,"%d%d",&x,&y);
movepen(x,y);
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
break;
}
case ’#’: /* input comment line */
{
readinfo();
break;
}
case ’L’: /* label at (x+dx,y+dy) */
{
fscanf(fp_input,"%d%d%d%d%s",&dx,&dy,&txt_size,&alpha,&info1);
print(x+dx,y+dy,alpha,txt_size,info1);
movepen(x,y);
break;
}
case ’C’: /* set pen color */
{
getcolor();
break;
}
case ’c’: /* capacitor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type=1 ) capacitor(x,y,pin,length,scale,rot);
break;
}
case ’d’: /* diode */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) diode(x,y,pin,length,scale,rot);
if( type==2 ) zener(x,y,pin,length,scale,rot);
break;
}
case ’g’: /* ground symbol */
{
fscanf(fp_input,"%d%d%d",&type,&scale,&rot);
if( type==1 ) g1(scale,rot);
if( type==2 ) g2(scale,rot);
102
if( type==3 ) g3(scale,rot);
break;
}
case ’y’: /* back up # nodes */
{
fscanf(fp_input,"%d",&rot);
x=xpoint[npoints-rot]; y=ypoint[npoints-rot];movepen(x,y);
break;
}
case ’r’: /* resistor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) resistor(x,y,pin,length,scale,rot);
break;
}
case ’w’: /* a wire */
{
fscanf(fp_input,"%d%d",&length,&rot);
wire(length,rot);
break;
}
case ’t’: /* text from (x,y) with size and angle */
{
fscanf(fp_input,"%d%d%d%d",&x,&y,&szinfo,&alpha);
fscanf(fp_input,"%c",&ch); /* eat up lf at end of input line */
readinfo();
print(x,y,alpha,szinfo,info);
break;
}
case ’x’: /* junction */
{
junction(x,y);
break;
}
case ’o’: /* output/input connector */
{
fscanf(fp_input,"%d%d%s",&scale,&alpha,&info1);
x1=5*scale;y1=0;rotate(&x1,&y1,rot);circle(x+x1,y+y1,5*scale,72);
if( alpha==180 ) print(x-20,y+12,0,text_scale,info1);
movepen(x,y); /* move pen back to connecting point (x,y) */
break;
}
} /* end of switch */
103
5 --- Node Checking
As we will see later on, the nodes are very critical to the successful
completion of a large schematic drawing. So I want a way to check where
the nodes are and make sure they are in the internal table. And an
option to display the nodes and their associated numbers.
So I’ll create a "D" command for debug or display of nodes. D1 for
just displaying the nodes and D2 for displaying and labeling the nodes
in the schematic. I think in this manner this option you can turn on
and off at will and aperiodically check your work. I typically do schematics
just as I used to do them by hand. A few components at a time and then
determine if I am going in the right direction.
I find that the incremental creation and checking saves me some time
and energy. With high speed microprocessor based home computers that
are faster than most supercomputers of a decade ago we have so much
computer power, let’s put it to use. I find that I can enter a section
of a schematic, save the file, then run:
schematic device_schematic_file
dxy2ps plotfile >device_schematic_file.ps
evince device_schematic_file.ps
Of course, the file names are not that long. Maybe something like
pvxo for the K8IQY PVXO circuit. But you get the idea. I also have
aliases dp and dl that invoke dxy2ps with options to create portrait
or landscape drawings.
OK. Here is the switch and the code to do the debugging option. I
modified the junction command switch also, so it is shown here to let
you update yours, if you want.
104
case ’x’: /* junction */
{
junction(x,y);
break;
}
case ’D’: /* display/debug node values */
{
fscanf(fp_input,"%d",&type);
if( type==1 ) nodecolors();
if( type==2 ) nodelabels();
break;
}
void junction(int xp,int yp)
{
movepen(xp-1,yp-1);line(xp+1,yp-1);line(xp+1,yp+1);line(xp-1,yp+1);line(xp-1,yp-1);
movepen(xp-2,yp-2);line(xp+2,yp-2);line(xp+2,yp+2);line(xp-2,yp+2);line(xp-2,yp-2);
movepen(xp-3,yp-3);line(xp+3,yp-3);line(xp+3,yp+3);line(xp-3,yp+3);line(xp-3,yp-3);
/* added diamond to give almost a circular effect */
movepen(xp,yp-4);line(xp+4,yp);line(xp,yp+4);line(xp-4,yp);line(xp,yp-4);movepen(xp,yp);
}
void nodecolors()
{
int i;
float red,green,blue;
/* set pen color to red */
red=1.0;green=0.0,blue=0.0;
fprintf(plotter,"J10 %8.3f %8.3f %8.3f\n",red,green,blue);
for( i=0; i<=npoints; i++ )
junction(xpoint[i],ypoint[i]);
/* set color back to black */
red=0.0;green=0.0,blue=0.0;
fprintf(plotter,"J10 %8.3f %8.3f %8.3f\n",red,green,blue);
}
void nodelabels()
{
int i;
float red,green,blue;
/* set pen color to red */
red=1.0;green=0.0,blue=0.0;
fprintf(plotter,"J10 %8.3f %8.3f %8.3f\n",red,green,blue);
for( i=0; i<=npoints; i++ )
{
junction(xpoint[i],ypoint[i]);
/* programming trick to convert int to character string */
sprintf(info,"%d",i);
/* write node number just to the upper right of the node */
print(xpoint[i]+10,ypoint[i]+10,0,2,info);
}
/* set color back to black */
red=0.0;green=0.0,blue=0.0;
}
105
Here is the data set to demo the D1 option.
200 200 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d1 1 140 3 0 D1 1N4001 x
c1 1 100 3 270 C1 0.1^uF g1 4 0
y1
w 90 0 x
c1 1 100 3 270 C2 10^uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity with C2
L -15 60 2 0 +
y1 w 80 0
# added debug to show nodes in red
D1
e
And here is the output:
106
Now change the D1 to D2 and rerun and we get:
And you can see the nodes and their respective numbers. One of the
surprises here is that nodes 4 and 5 are the same. Well, sure enough.
I had an extra nodes++ in the diode routine itself, so I fixed it and
here is the new and greatest code results:
Later I will add another command to allow me to go direct to a node
using the node number. In Jim Kortge’s original schematic you will
note that we will need to come back to node 4 or 6 and start a wire
that goes off to another part of the circuit. We now can do this easily
IMHO.
107
6 — regulator
Now we can finally get to the regulator. The regulator is just a
simple rectangle. We have a input, a common, and an output terminal.
Since most of the time the common is connected to ground, I’m going
to take a chance and set it up that to be drawn automatically. And
because of my experience I also know that most likely the ground is
100 units from the center of the rectangle as I use length=100 for most
components. So here is my layout for the R for regulator command:
b b1 2
Node 1 is going to be the current (x,y) position in the schematic
where the regulator will be attached to the rest of the circuit. I’ll
draw the square scaled by the scale factor, add the ground line and
the ground symbol and the leave the pen at node 2. This will allow
me to add the wire or component that leads off to the right. No need
for me to draw the line to the right as it would probably be the wrong
length anyway.
108
So here is the code:
void regulator(int xs, int ys, int scale, int angle)
{
int xp,yp;
xp=0*scale;yp=10*scale;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
xp=20*scale;yp=10*scale;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
xp=20*scale;yp=-10*scale;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
xp=0*scale;yp=-10*scale;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
/* ground wire to ground symbol */
xp=10*scale;yp=-10*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);
xp=10*scale;yp=-100;rotate(&xp,&yp,rot); line(xs+xp,ys+yp);
print(xs+8*scale,ys+22*scale,0,2,info1);
print(xs-2*scale,ys+14*scale,0,2,info2);
x=xs+xp;y=ys+yp; g1(4,0);
x=xs+20*scale;y=ys;movepen(x,y); /* move pen to right center of square */
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
}
And this gives the nice looking results:
and the last lines in the input for the PVXO schematic.
# show polarity with C2
L -15 60 2 0 +
y1 w 80 0
R 3 0 U1 UA78L09
w 80 0
e
109
Now we want to add the next two caps in the schematic. You’ll note
they look exactly the same as C1 and C2, so save yourself some typing
by copying the lines for C1 and C2 at the end of the input file just
before the e for end of input. Change the text for the labels of the
components and see what you get.
110
7 — JFETs
Now we get to a real silicon device with three leads, a JFET. The
first one we encounter is an NPN JFET. In this case a 2N5484 and it’s
labeled Q1. If you look down below it in another part of the schematic
you find another one, Q2. They are oriented differently. Now, in my
old code I used my own technique of having a JFET and I’d rotate it
to the proper orientation and sometimes I had to displace it by some
amount to get the proper lead to the current node.
I think this time I can have the program do it. Let’s see and maybe
if you are a programmer you can give me ideas if my way isn’t the best
way.
First: here is the JFET drawn in a typical orientation.
bb1
b 3
b 2
b
You can play with the geometry to get a figure that you find more
pleasing. I may do something after I get one drawn into the current
schematic. This is just starting point.
I have marked the three lead points where other wires and other components
may connect to the JFET and labeled the points with numbers. The red
dot is the center of the circle.
111
Now comes the hard part. If you are at a point (x,y) and one of
the leads is at that point and the part is oriented at 0, 90, 180, or
270 degrees, how would you draw the part using the movepen, rotate,
and line functions we already know? Not an easy answer is it?
Well, it may be if you think relative to the geometric center where
the red dot is. First, what are the displacements from each node to
the red dot with 0 degree orientation?
Pin 1. (6*scale,0)
Pin 2. (-6*scale,3)
Pin 3. (-6*scale,-3)
These three vectors (dx,dy) represent a relative displacement from
the current node. I rotate the appropriate displacement about the current
point and I then draw the JFET from the red dot at this resulting position
with the rotation angle used to get the new center point. After you
study the code you can see how it works. So here goes my attempt at
this.
Oh. The command line input will be of the form
j1 pin# scale angle info1 info2
Here is the switch statement:
case ’j’: /* JFET 1=NPN 2=PNP */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) jfetNPN(x,y,pin,scale,rot);
break;
}
112
Here is the complete function.
void jfetNPN(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-6*scale;yp=3*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-6*scale;yp=-3*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for center of NPN jfet */
x_center=xs+xp;y_center=ys+yp;
circle(x_center,y_center,5*scale,72); /* circle about the center with 36 segments */
/* draw line from pin 1 to arrow for G */
xp=-6*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
/* while at pin 1 set a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw arrow since we are back at center of circle */
xp=0*scale;yp=1*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=2*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=0*scale;yp=-1*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* now draw vertical line in symbol */
xp=2*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=2*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* now draw horizontal lines for D and S gates */
xp=2*scale;yp=-3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=-3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 2 set a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
xp=2*scale;yp=3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 set a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
print(x_center+0*scale,ys+12*scale,0,2,info1);
print(x_center+0*scale,ys+8*scale,0,2,info2);
}
113
Here is the schematic on the first try. Pretty close, but this will
happen from time to time and all you have to do is go back and lengthen
a wire or two and the spacing comes out better.
114
OK. I want to thank you for keeping me going on this project. I
wrote my first schematic routine almost 20 years ago and have been using
it ever since. But in the last week I have started from scratch on
this project to write up how I did it. And in the process I came up
with a couple of new ideas and methods for doing this. The orientation
and drawing of the JFET is new and will do the same think for transistors
also, so this is worth the effort alone for me to do this.
Here is the data set and associated output.
200 200 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0
w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d1 1 140 3 0 D1 1N4001 x
c1 1 100 3 270 C1 0.1^uF g1 4 0
y1 w 90 0 x
c1 1 100 3 270 C2 10uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity for C2
L -15 60 2 0 +
y1 w 80 0
R 3 0 U1 UA78L09
w 80 0 x
c1 1 100 3 270 C3 0.1^uF g1 4 0
y1 w 90 0 x
c1 1 100 3 270 C4 100^uF g1 4 0
# label C4 with its working voltage
L 12 10 2 0 16V
# show polarity for C4
L -15 60 2 0 +
y1 r1 1 140 3 0 R1~100 ~ x
c1 1 100 3 270 C5 0.1uF g1 4 0
y1 w 140 0 w 30 270 x
j1 2 6 180 U2 2N5484
# show nodes to double check jfet routine
D1
e
115
Now if you want, comment out the circle function call in jfetNPN
and this will eliminate the circle if you don’t like the tiny imperfections
due to stepping of pens in plotters. And you get:
OK, now it’s your turn. Get your program up to this point. I’ll
wait. Take your time and do things one section at a time. Let me know
all the typos you find in my stuff. I modify stuff and may have forgotten
to put it in this document. Thanks for your patience.
How does this compare with Jim Kortge’s schematic for the PVXO circuit
diagram? Getting close? No, but it’ll be fun getting there. I’m going
off to work on something else for a week while you catch up.
September 9, 2007 Sunday
116
100 100 2
# plot series of NPN JFETs
# with different orientations
# and pin number connection points
G 0 800 x j1 1 6 0 U1 2N5484
G 250 800 x j1 1 6 90 U2 2N5484
G 500 800 x j1 1 6 180 U3 2N5484
G 750 800 x j1 1 6 270 U4 2N5484
G 0 600 x j1 2 6 0 U1 2N5484
G 250 600 x j1 2 6 90 U2 2N5484
G 500 600 x j1 2 6 180 U3 2N5484
G 750 600 x j1 2 6 270 U4 2N5484
G 0 400 x j1 3 6 0 U1 2N5484
G 250 400 x j1 3 6 90 U2 2N5484
G 500 400 x j1 3 6 180 U3 2N5484
G 750 400 x j1 3 6 270 U4 2N5484
e
117
The previous page was produced with the JFET routine as follows.
We will have to come up with a clever scheme for placing the labels
in the correct position later on.
void jfetNPN(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-6*scale;yp=3*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-6*scale;yp=-3*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for center of NPN jfet */
x_center=xs+xp;y_center=ys+yp;
circle(x_center,y_center,5*scale,72); /* circle about the center with 72 segments */
/* draw line from pin 1 to arrow for G */
xp=-6*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
/* while at pin 1 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw arrow since we are back at center of circle */
xp=0*scale;yp=1*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=2*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=0*scale;yp=-1*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* now draw vertical line in symbol */
xp=2*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=2*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* now draw horizontal lines for D and S gates */
xp=2*scale;yp=-3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=-3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 2 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
xp=2*scale;yp=3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
if( rot==0 ) { print(x_center+0*scale,ys+12*scale,0,2,info1);
print(x_center+0*scale,ys+8*scale,0,2,info2); }
if( rot==90 ) { print(x_center+9*scale,ys+9*scale,0,2,info1);
print(x_center+9*scale,ys+5*scale,0,2,info2); }
if( rot==180 ) { print(x_center-2*scale,ys+12*scale,0,2,info1);
print(x_center-2*scale,ys+8*scale,0,2,info2); }
if( rot==270 ) { print(x_center+9*scale,ys-5*scale,0,2,info1);
print(x_center+9*scale,ys-9*scale,0,2,info2); }
}
118
8 — inductors
If we go back to drawing the K8IQY schematic we are now at a point
where we need an inductor. Since I rarely use air core inductors any
more I’ll start with a simple iron core inductor. Here is the results
for one drawn using my grid scale:
b1 b2
When you go to write the function to draw the above you’ll find that
there are a large number of nodes, but it can’t be helped right now.
We’ll do this without any for loop structures in the code. Just to
make things easier to write and easier to see. I hope.
Here is the case to input the inductor parameters:
case ’l’: /* inductor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) inductor(x,y,pin,length,scale,rot);
break;
}
Here is the function inductor.
void inductor(int xs,int ys,int pin,int length,int scale,int rot)
119
{
int x1,y1,halflength,xpin,ypin;
if( pin==1 ) {x1=0;y1=0;}
if( pin==2 ) {x1=length;y1=0;}
rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1;
halflength=length/2;
movepen(xpin,ypin);
x1=halflength-6*scale+0*scale;y1=6*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-6*scale+12*scale;y1=6*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+0*scale;y1=8*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-6*scale+12*scale;y1=8*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
movepen(xpin,ypin);
x1=halflength-6*scale;y1=0; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+0*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+1*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+2*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+2*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+3*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+3*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+3*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+4*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+5*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+6*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+6*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+6*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+7*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+8*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+9*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+9*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+9*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+10*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+11*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+12*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-6*scale+12*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=length;y1=0; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x=xpin+x1; y=ypin+y1;
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
label(xpin+10,ypin,length,scale,rot,info1,info2);
movepen(x,y);
}
120
At this time I am a small distance away from the horizontal line
to the gate G of Q1 and the way to get to it is to add a wire 30 units
long at 270 degrees. Sometimes the length of the last component, C8
in this case, isn’t quite right and the two leads won’t line up. Just
go back and adjust the length of C8 by +1 or -1 to get perfect alignment
as shown below.
We are starting to crowd the page with the horizontal length taken
by the circuit. I’ll just scale it and later we’ll add a input parameter
to the dxy2ps utility to rotate 90 degrees on the page and go from portrait
to landscape mode.
I changed the labeling on L1 to put the text to the left of the symbol
for 270 degree rotations.
121
Here is the data set that produced this figure.
200 200 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0 w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d1 140 3 0 D1 1N4001 x
c1 100 3 270 C1 0.1uF g1 4 0
y1
w 90 0 x
c1 100 3 270 C2 10uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity for C2
L -15 60 2 0 +
y1 w 80 0
R 3 0 U1 UA78L09
w 80 0 x
c1 100 3 270 C3 0.1uF g1 4 0
y1
w 90 0 x
c1 100 3 270 C4 100uF g1 4 0
# label C4 with its working voltage
L 12 10 2 0 16V
# show polarity for C4
L -15 60 2 0 +
y1 r1 140 3 0 R1~100 ~ x
c1 100 3 270 C5 0.1uF g1 4 0
y1 w 140 0 w 30 270 x
j1 2 6 180 Q1 2N5484
x w 40 270 x
# now put inductor and ground
l1 1 100 3 270 L1 100uH g1 4 0
y1 w 90 0 x
c1 100 3 270 C7 100 g1 4 0
y1
c1 57 3 90 C8 100 x
w 30 180 y1 # move back to top of C8
w 90 0 x r1 100 4 270 R3 100K g1 4 0 y1
w 90 0 x d1 100 4 270 D1 1N4148 g1 4 0
e
122
9. — Crystals
Next component in the chain is a crystal. Pretty simple component.
Before I draw it here I want to change the methodology just a little.
I’ll go back and change the components that haven’t been done this way.
We’ll number all the connecting points in a part even those with just
two, such as a resistor or cap. Then when we input the part we input
the pin that is connected to the current node. This allows us to select
the pin and then the orientation. I had not done this for the diode.
So if we need to connect the cathode end at a node instead of the anode,
how will we do it. Previously I moved from the current node to the
point where the cathode would be and drew the diode from there with
the proper orientation. An inconvience, but bearable. Now we don’t
have to do that. Just select the end and set the orientation and draw.
OK. Now for the crystal symbol.
b1 b 2
case ’X’: /* crystal */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) crystal(x,y,pin,length,scale,rot);
break;
}
123
void crystal(int xs,int ys,int pin,int length,int scale,int rot)
{
int x1,y1,halflength,xpin,ypin;
if( pin==1 ) x1=0;y1=0;
if( pin==2 ) x1=length;y1=0;
rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1; /* coordinates of pin */
halflength=length/2;
x1=halflength-2*scale+0*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw plate to the left of crystal slab */
x1=halflength-2*scale+0*scale;y1=-3*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-2*scale+0*scale;y1=+3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw crystal slab */
x1=halflength-2*scale+1*scale;y1=-3*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-2*scale+1*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-2*scale+3*scale;y1=3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-2*scale+3*scale;y1=-3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-2*scale+1*scale;y1=-3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw plate to the right of crystal slab */
x1=halflength-2*scale+4*scale;y1=-3*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-2*scale+4*scale;y1=+3*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-2*scale+4*scale;y1=0*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=length;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
movepen(xpin+x1,ypin+y1);
if( rot==0 ) label(xpin,ypin,length,scale,rot,info1,info2);
x=xpin+x1;y=ypin+y1;movepen(x,y);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
}
Here is the schematic scaled down a bit:
124
125
10. — DPDT switch
Next comes the switches used to switch in and out inductors L2-L6.
These are DPDT, so here is the diagram for one of them including the
nodes needed for external connections.
b
b
b
b
1 3
2 4
On the next page is the schematic up to the point where I need a
varactor diode component. I have shifted using dxy2ps from portrait
to landscape mode by using the command:
dxy2ps -s 1.5 -x 30 -y 40 plotfile >out.ps
On the next page after the schematic I have the input data set that
created this figure. Study it and start where your program data file
is currently and add one line at a time and checking your program to
determine if it is doing the same thing. This will probably require
some debugging and additional programming on your part.
Doesn’t the output look pretty good?
126
127
0 200 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0 w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d1 1 140 3 0 D1 1N4001 x
c1 1 100 3 270 C1 0.1^uF g1 4 0
y1
w 90 0 x
c1 1 100 3 270 C2 10^uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity for C2
L -15 60 2 0 +
y1 w 80 0
R 3 0 U1 UA78L09
w 80 0 x
c1 1 100 3 270 C3 0.1^uF g1 4 0
y1
w 90 0 x
c1 1 100 3 270 C4 100^uF g1 4 0
# label C4 with its working voltage
L 12 10 2 0 16V
# show polarity for C4
L -15 60 2 0 +
y1 r1 1 140 3 0 R1~100 ~ x
c1 1 100 3 270 C5 0.1^uF g1 4 0
y1 w 140 0 w 30 270 x
j1 2 6 180 Q1 2N5484
x w 40 270 x
# now put inductor with ground
l1 1 100 3 270 L1 100^uH g1 4 0
y1 w 90 0 x
c1 1 100 3 270 C7 100 g1 4 0
y1
c1 1 57 3 90 C8 100 x
w 30 180 y1 # move back to top of C8
w 90 0 x r1 1 100 4 270 R3 100K g1 4 0 y1
w 90 0 x d1 1 100 3 270 D1 1N4148 g1 4 0 y1
# now add crystal
128
w 90 90 X1 1 100 6 0 REFERENCE Y1
w 90 270 x c1 1 100 3 270 C8 4.7pF g1 4 0 y1
w 100 0 w 30 270
# switch #1
L 10 40 3 0 S1
S1 1 6 0 ~ ~
# go to pin 2
y2 L 0 -65 2 0 L2 L 0 -90 2 0 2.2uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #2
L 10 40 3 0 S2
S1 1 6 0 ~ ~
# go to pin 2
y2 L 0 -65 2 0 L3 L 0 -90 2 0 4.7uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #3
L 10 40 3 0 S3
S1 1 6 0 ~ ~
# go to pin 2
y2 L 0 -65 2 0 L4 L 0 -90 2 0 10^uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #4
L 10 40 3 0 S4
S1 1 6 0 ~ ~
# go to pin 2
y2 L 0 -65 2 0 L5 L 0 -90 2 0 22uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #5
L 10 40 3 0 S5
S1 1 6 0 ~ ~
# go to pin 2
y2 L 0 -65 2 0 L6 L 0 -90 2 0 39uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 80 0 x
c1 1 100 3 270 C9 30pF g1 4 0 y1 w 90 0 x
# varicap D2 comes next
e
129
Here is the switch statement for the main function.
case ’S’: /* switch */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) switch1(x,y,pin,scale,rot);
break;
}
Here is the function to draw the DPDT switch.
void switch1(int xs,int ys,int pin,int scale,int rot)
{
/* this routine is very basic DPDT switch
switch is oriented in only one direction
can not be rotated
drawn for K8IQY circuit at the present time
and most likely will not change any time soon */
int x1,y1,halflength,xpin,ypin;
x1=0;y1=0; rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1; /* coordinates of pin 1 */
movepen(xpin,ypin-1*scale); circle(xpin,ypin+y1-1*scale,1*scale,72);
movepen(xpin,ypin+y1-8*scale); circle(xpin,ypin+y1-8*scale,1*scale,72);
movepen(xpin+1*scale,ypin-8*scale); line(xpin-2*scale,ypin-1*scale);
/* shorting line */
movepen(xpin+1*scale,ypin-8*scale); line(xpin+7*scale,ypin-8*scale);
movepen(xpin+9*scale,ypin-8*scale); line(xpin+6*scale,ypin-1*scale);
movepen(xpin,ypin-15*scale); circle(xpin,ypin-15*scale,1*scale,72);
movepen(xpin+8*scale,ypin-1*scale); circle(xpin+8*scale,ypin-1*scale,1*scale,72);
movepen(xpin+8*scale,ypin-8*scale); circle(xpin+8*scale,ypin-8*scale,1*scale,72);
movepen(xpin+8*scale,ypin-15*scale); circle(xpin+8*scale,ypin-15*scale,1*scale,72);
npoints++;xpoint[npoints]=xpin;ypoint[npoints]=ypin; /* pin 1 */
npoints++;xpoint[npoints]=xpin;ypoint[npoints]=ypin-16*scale; /* pin 2 */
npoints++;xpoint[npoints]=xpin+8*scale;ypoint[npoints]=ypin; /* pin 3 */
npoints++;xpoint[npoints]=xpin+8*scale;ypoint[npoints]=ypin-16*scale; /* pin 4 */
}
130
11. — varactor diode
The varactor diode is like the rectifier diode but has an extra line
separated from the cathode to show capacitance. There are different
symbols for this component and some have an arrow to indicate variable
component capability, but I choose not to add it. But you can if you
so desire. Here is my version.
b1 b 2
Here is the case for the switch statement in the main function.
case ’d’: /* diode */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) diode(x,y,pin,length,scale,rot);
if( type==2 ) zener(x,y,pin,length,scale,rot);
if( type==3 ) varacap(x,y,pin,length,scale,rot);
break;
}
131
Here is the code.
void varacap(int xs,int ys,int pin,int length,int scale,int rot)
{
int x1,y1,halflength,xpin,ypin;
if( pin==1 ) x1=0;y1=0;
if( pin==2 ) x1=-length;y1=0;
rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1; /* coordinates of pin */
movepen(xpin,ypin);
halflength=length/2;
x1=halflength-4*scale+0*scale;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw arrow */
x1=halflength-4*scale+0*scale;y1=-4*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-4*scale+0*scale;y1=+4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-4*scale+6*scale;y1=+0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=halflength-4*scale+0*scale;y1=-4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw cathode slab */
x1=halflength-4*scale+6*scale;y1=-4*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-4*scale+6*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* draw cap */
x1=halflength-4*scale+8*scale;y1=-4*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=halflength-4*scale+8*scale;y1=4*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* move to center of vertical line */
x1=halflength-4*scale+8*scale;y1=0*scale; rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=length;y1=0*scale; rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x=xpin+x1;y=ypin+y1;movepen(xpin+x1,ypin+y1);
if( rot==0 ) label(xpin,ypin,length,scale,rot,info1,info2);
if( pin==2 ) x=xpin;y=ypin;
movepen(x,y);
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
}
132
To do a small sanity check. Here are some segments of code to compare
with yours. They do not have to match exactly. Your style may not
be compatable with mine, but you can see how yours compares and maybe
if you are having a problem you can find it with another code to compare
with.
Here is the switch statement in the main function:
/* input commands until EOF or e character in input stream */
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’D’: /* display/debug node values */
{
fscanf(fp_input,"%d",&type);
if( type==1 ) nodecolors();
if( type==2 ) nodelabels();
break;
}
case ’G’: /* go direct to point (x,y) */
{
fscanf(fp_input,"%d%d",&x,&y);
movepen(x,y);
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
break;
}
case ’#’: /* input comment line */
{
readinfo();
break;
}
case ’L’: /* label at (x+dx,y+dy) */
{
fscanf(fp_input,"%d%d%d%d%s",&dx,&dy,&txt_size,&alpha,&info1);
print(x+dx,y+dy,alpha,txt_size,info1);
movepen(x,y);
break;
}
133
case ’C’: /* set pen color */
{
getcolor();
break;
}
case ’R’: /* regulator such as 78L05 */
{
fscanf(fp_input,"%d%d%s%s",&scale,&rot,&info1,&info2);
regulator(x,y,scale,rot);
break;
}
case ’S’: /* switches 1=DPDT */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) switch1(x,y,pin,scale,rot);
break;
}
case ’X’: /* crystal */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) crystal(x,y,pin,length,scale,rot);
break;
}
case ’c’: /* capacitor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type=1 ) capacitor(x,y,pin,length,scale,rot);
break;
}
case ’d’: /* diode */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) diode(x,y,pin,length,scale,rot);
if( type==2 ) zener(x,y,pin,length,scale,rot);
if( type==3 ) varacap(x,y,pin,length,scale,rot);
134
break;
}
case ’g’: /* ground symbol */
{
fscanf(fp_input,"%d%d%d",&type,&scale,&rot);
if( type==1 ) g1(scale,rot);
if( type==2 ) g2(scale,rot);
if( type==3 ) g3(scale,rot);
break;
}
case ’j’: /* JFET 1=NPN 2=PNP */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) jfetNPN(x,y,pin,scale,rot);
break;
}
case ’l’: /* inductor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) inductor(x,y,pin,length,scale,rot);
break;
}
case ’y’: /* back up # nodes */
{
fscanf(fp_input,"%d",&rot);
x=xpoint[npoints-rot]; y=ypoint[npoints-rot];movepen(x,y);
break;
}
case ’r’: /* resistor */
{
fscanf(fp_input,"%d%d%d%d%d%s%s",&type,&pin,&length,&scale,&rot,&info1,&info2);
if( type==1 ) resistor(x,y,pin,length,scale,rot);
break;
}
case ’w’: /* a wire */
135
{
fscanf(fp_input,"%d%d",&length,&rot);
wire(length,rot);
break;
}
case ’t’: /* text from (x,y) with size and angle */
{
fscanf(fp_input,"%d%d%d%d",&x,&y,&szinfo,&alpha);
fscanf(fp_input,"%c",&ch); /* eat up lf at end of input line */
readinfo();
print(x,y,alpha,szinfo,info);
break;
}
case ’x’: /* junction */
{
junction(x,y);
break;
}
case ’o’: /* output/input connector */
{
fscanf(fp_input,"%d%d%s",&scale,&alpha,&info1);
x1=5*scale;y1=0;rotate(&x1,&y1,rot);circle(x+x1,y+y1,5*scale,72);
if( alpha==180 ) print(x-20,y+12,0,text_scale,info1);
movepen(x,y); /* move pen back to connecting point (x,y) */
break;
}
} /* end of switch */
136
Here are all the current function prototypes at the beginning of
the source code.
/* function prototype definitions */
void exit(int);
void home();
void movepen(int,int);
void line(int,int);
void getcolor();
void resistor(int,int,int,int,int,int);
void rotate(int *,int *,int);
void rotatel(int,int,int,int *,int *);
void g1(int,int);
void g2(int,int);
void g3(int,int);
void wire(int,int);
void print(int,int,int,int,char *);
int char_length(char info1[]);
void label(int,int,int,int,int,char *,char *);
void capacitor(int,int,int,int,int,int);
void circle(int,int,int,int);
void diode(int,int,int,int,int,int);
void zener(int,int,int,int,int,int);
void junction(int,int);
void nodecolors();
void nodelabels();
void regulator(int,int,int,int);
void jfetNPN(int,int,int,int,int);
void inductor(int,int,int,int,int,int);
void crystal(int,int,int,int,int,int);
void switch1(int,int,int,int,int);
void varacap(int,int,int,int,int,int);
137
12. — variable resistor
We get to a new symbol we need now, the variable resistor. It is
like the fixed resistor symbol except we have another terminal and we
need an arrow showing the variable point. So here is my rendition of
same. I have made the variable point the origin of the component, so
all pen movements are relative to this origin.
b1
b
2
b3
138
Here is the function for the variable resistor. I made the arrow
slightly larger than the diagram shows just to make things a little
bit nicer to see.
void varresistor(int xs,int ys,int pin,int length,int scale,int rot)
{
int x1,y1,halflength,xpin,ypin;
halflength=length/2;
if( pin==1 ) {x1=0;y1=0;}
if( pin==2 ) {x1=-9*scale;y1=7*scale+halflength;}
if( pin==3 ) {x1=-9*scale;y1=-5*scale-halflength;}
rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1; /* coordinates of pin 1 */
npoints++;xpoint[npoints]=xpin;ypoint[npoints]=ypin;
/* now at origin of part */
x1=3*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=3*scale;y1=-2*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=3*scale;y1=2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=6*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=3*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* move to base point */
x1=9*scale;y1=-halflength;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 2 in node list */
label(xpin+x1,ypin+y1,length,scale,rot-90,info1,info2);
movepen(xpin+x1,ypin+y1);
x1=9*scale;y1=-7*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=6*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=6*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=6*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=halflength;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 3 in node list */
x=xpin+x1;y=ypin+y1;movepen(x,y);
}
139
140
13. — goto node
At this time, if you use dxy2ps -s 1.5 plotfile >out.ps the postscript
version of the schematic you’ll see that we pretty much take up length
of the page. If yours is too much, then look for horizontal wires that
you can reduce the length by 10 or 20 units. I do this all the time.
At this time the schematic is complete for the first row of components.
We now need to go back to the node at Q1 and L1. So first of all, use
the D2 debug/display node values command to find out what the node number
is for this connection. I get a value of 21, but yours may vary depending
upon your choice of wires and components...
So now we need to add a command to get to the node direct and that
magic command is going to be PN where N is the node number. Here we
want P21 for my schematic.
The segment of code turns out to be very simple. We just add the
following to the switch statement in the main function.
case ’P’: /* go to point # */
{
fscanf(fp_input,"%d",&npoint);
x=xpoint[npoint];
y=ypoint[npoint];
/* add the node to the end of the list */
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
movepen(x,y);
break;
}
and the end of the input data set looks like:
w 90 0
r2 1 100 4 0 POT1 5K-10T
o 1 90 9V
y1 g1 4 0
# now back up to node 23
C 1.0 0.0 0.0
P21 # goto to junction at Q1 and L1
141
w 90 180 c1 360 3 270 C11 10pF x
C 0.0 0.0 0.0
#
e
and the schematic now looks like:
142
From the new schematic you can see that the goto node function works
perfectly. We are now ready to continue on with the schematic. I want
to modify Jim’s work in the following manner. You’ll note that coming
off of Q2 D there is a wire that goes back to the left and crosses over
the lead from C11 and goes to R7 and C12. Then to 9V. I want to just
go up from Q2 the across the entire schematic and then around the POT1
to the 9V connection and then to the 9V output of the regulator. This
is fairly easy to do, but you may have to go through a couple of iterations
to get the distances correct.
Here is the data set complete and multiple components per line, which
you can separate out easily with an editor if you copy to a file....
0 800 2
# K8IQY PVXO schematic from the web
# drawn by K7QO as part of a tutorial
g1 4 0 w 40 90 w 20 180 o 1 180 GND
G -20 100 o 1 180 +12V
# D1 is a reverse polarity protection diode
d1 1 140 3 0 D1 1N4001 x c1 1 100 3 270 C1 0.1uF g1 4 0
y1 w 90 0 x c1 1 100 3 270 C2 10uF g1 4 0
# label C2 with its working voltage
L 12 10 2 0 16V
# show polarity for C2
L -15 60 2 0 + y1 w 80 0 R 3 0 U1 UA78L09
w 80 0 x c1 1 100 3 270 C3 0.1uF g1 4 0 y1
w 90 0 x c1 1 100 3 270 C4 100uF g1 4 0
# label C4 with its working voltage
L 12 10 2 0 16V
# show polarity for C4
L -15 60 2 0 +
y1 r1 1 140 4 0 R1~100 ~ x c1 1 100 3 270 C5 0.1uF g1 4 0
y1 w 140 0 w 30 270 x j1 2 6 180 Q1 2N5484
x w 40 270 x
# now put inductor with ground
l1 1 100 3 270 L1 100uH g1 4 0 y1 w 90 0 x
c1 1 100 3 270 C7 100 g1 4 0 y1 c1 1 57 3 90 C8 100 x
w 30 180 y1 # move back to top of C8
w 90 0 x r1 1 100 4 270 R3 100K g1 4 0 y1
w 90 0 x d1 1 100 3 270 D1 1N4148 g1 4 0 y1
# now add crystal
w 90 90 X1 1 100 6 0 REFERENCE Y1
w 90 270 x c1 1 100 3 270 C8 4.7pF g1 4 0 y1
w 100 0 w 30 270
# switch #1
L 10 40 3 0 S1 S1 1 6 0 ~ ~ # go to pin 2
y2 L 0 -65 2 0 L2 L 0 -90 2 0 2.2uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #2
L 10 40 3 0 S2 S1 1 6 0 ~ ~ # go to pin 2
y2 L 0 -65 2 0 L3 L 0 -90 2 0 4.7uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
143
y4 w 30 90 w 60 0 w 30 270
# switch #3
L 10 40 3 0 S3 S1 1 6 0 ~ ~ # go to pin 2
y2 L 0 -65 2 0 L4 L 0 -90 2 0 10uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #4
L 10 40 3 0 S4 S1 1 6 0 ~ ~ # go to pin 2
y2 L 0 -65 2 0 L5 L 0 -90 2 0 22uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 60 0 w 30 270
# switch #5
L 10 40 3 0 S5 S1 1 6 0 ~ ~ # go to pin 2
y2 L 0 -65 2 0 L6 L 0 -90 2 0 39uH w 40 270 l1 1 48 3 0 ~ ~ w 40 90
y4 w 30 90 w 80 0 x
c1 1 100 3 270 C9 30pF g1 4 0 y1 w 90 0 x
# varicap D2
d3 2 100 3 90 D2 MV309 g1 4 0 y1 r1 1 120 4 0 R4 47K x
c1 1 100 3 270 C10 0.1uF g1 4 0 y1 w 90 0 x
r1 1 100 4 270 R5 1.8K g1 4 0 y1 w 90 0
r2 1 100 4 0 POT1 5K-10T w 100 90 x 0 L 0 20 2 0 9V
y2 g1 4 0
# now back up to node 21
P 21
w 90 180 c1 1 360 3 270 C11 10pF x r1 1 100 4 270 R6 100k g1 4 0
y1 w 40 0 j1 1 6 0 Q2 2N5484
w 120 90 w 80 0 x c1 1 100 3 270 C12 0.1uF g1 4 0 y1 r1 1 220 4 0 R7 100
x L 0 20 2 0 9V w 1300 0 w 430 90 w 1990 180 L 0 20 2 0 9V w 200 270
#
e
144
145
Now drawing schematics is somewhat of an art form and what pleases
one may not please another. I prefer, where possible, to show all connecting
lines. In building a circuit I will print off multiple copies and one
I will highlight wires and components as I connect them and with all
the wires in place I highlight them also. I find that it helps reduce
and almost eliminate mistakes and it makes you think about what you
are doing.
OK. Here is the last of the file and the schematic. This will get
us to the transformer, which we don’t have.
r1 1 100 4 270 R6 100k g1 4 0
y1 w 40 0
j1 1 6 0 Q2 2N5484
w 120 90 w 80 0 x c1 1 100 3 270 C12 0.1uF g1 4 0 y1 r1 1 220 4 0 R7 100
x L 0 20 2 0 9V w 1300 0 w 430 90 w 1990 180 L 0 20 2 0 9V w 100 270
y9 w 50 270 x l1 1 100 3 270 L7 100uH g1 4 0
y1 c1 1 100 3 0 C13 0.1uF x r1 1 100 4 0 R8 560 x r1 1 100 4 270 R9 56 g1 4 0
y1 c1 1 120 3 90 C14 0.01uF w 80 0
#
e
BTW. I’m using
dxy2ps -l 0.5 -s 1.5 plotfile > plot043.ps
to create the postscript file. The -l 0.5 option to dxy2ps sets the
starting pen size to 0.5mm. The finer lines may help in generating
and reading more complex schematics, but don’t make the lines too thin.
Been there. Done that. See the dxy2ps document in the reference material
at the end of this document.
146
147
14 — transformer
b1
b2 b 3
b 4
b 5
With this new transformer I will draw the schematic up until the
point where we need a transistor. See next page for the results.
148
149
Here is the function xformer01 for the transformer. There are a
lot of straight lines, so a lot of lines of code. A for loop would
probably simplify this but what the hey. We get paid by the line....
(smiley goes here)
void xformer01(int xs,int ys,int pin,int scale,int rot)
{
int x1,y1,xpin,ypin;
if( pin==1 ) {x1=0;y1=0;}
if( pin==2 ) {x1=0*scale;y1=12*scale;}
if( pin==3 ) {x1=-14*scale;y1=12*scale;}
if( pin==4 ) {x1=-14*scale;y1=6*scale;}
if( pin==5 ) {x1=-14*scale;y1=0*scale;}
rotate(&x1,&y1,rot); movepen(xs+x1,ys+y1);
xpin=xs+x1;ypin=ys+y1; /* coordinates of pin 1 */
npoints++;xpoint[npoints]=xpin;ypoint[npoints]=ypin;
movepen(xpin,ypin); /* move to upper right of xformer */
x1=4*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-1*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=2*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-3*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=5*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=2*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-6*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=5*scale;y1=-7*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-8*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-9*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=2*scale;y1=-9*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-9*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=5*scale;y1=-10*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-11*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-12*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=0*scale;y1=-12*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
/* now draw two vertical lines for transformer core */
x1=6*scale;y1=-12*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=6*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=8*scale;y1=-12*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=8*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* now draw secondary windings */
x1=14*scale;y1=-12*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
x1=10*scale;y1=-12*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-11*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-10*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-9*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=-9*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-9*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-8*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-7*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=14*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
150
x1=10*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-1*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=14*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1;
}
151
15. — transistor
The transistor is much like the JFET symbol, so we’ll use some of
that code in building the function transNPN.
b1
b 3
b 2
With the code for transistor and with one addition of a M for move
function that allows me to move (dx,dy) from the current point to allow
me to generate broken lines like that from the center tap of the transformer
to the right. I personally do not like lines crossing and I guess it
is from the old school of schematics.
Here is the almost final version of the K8IQY PVXO circuit. Just
needs some fine tuning on some labeling that the program currently doesn’t
generate. But you have to admit it does look pretty darn good.
152
153
16. — Uses for Labeling Relative to Nodes
In our schematic for the Jim Kortge K8IQY PVXO we could use some
labeling in some key places. The problem with writing and even using
software is that there is some human intervention always required to
personalize the results or even just take care of cases that the software
doesn’t manage. I don’t mind adding touches myself and that is one
of the reasons I added the L for labeling offset command. Here is the
transformer by itself. I have marked the nodes with a junction (x)
symbol.
The distance from the bottom horizontal lines to the top is only
14 units times the scale factor. Let’s assume a scale factor of 6,
which is what I used for the previous schematic, which means a distance
of 8.4mm. In drawing schematics I think in the unit scale, thus the
transformer is 84 units tall. That gives me a starting value to use.
For the offset labeling using the L command
L dx dy scale angle text_without_blanks
I can add to the transformer any text that I want. This part of
the tutorial will illustrate the power of the command. Here is the
file that produced the transformer above.
200 200 2
T1 1 6 0 T1 ~
P1 x
P2 x
P3 x
P4 x
P5 x
e
154
Remember the P command takes us to the node number that follows it.
By the way. The node number can be spaced away from the P, if you so
desire for readability. Right after the junction command, x, think
about where the pen is located. It is located at the point at the end
of one of the windings of the transformer. This is important.
I can do all the labeling with the pen at one of the nodes or I can
do it at each of the points. I prefer to do it from one place to keep
all the labeling commands in one place for the component. I can spot
them easily and I know what they do.
So, let’s label the transformer to correspond to what Jim has. Bring
up his diagram on the computer and on scratch paper (this is what I
do unless I print off the original schematic or have a copy handy on
paper) draw the transformer and all the labels.
I’m going to do all the labeling assuming that I am at node 1, the
upper left terminal of the transformer. Look at your code to see what
the distances are between the points. I know from my code it is 12
units from node 1 to 2 and 14 units from node 1 to 5 (the top right
terminal). With a scale factor of 6, these become 96 and 102 respectively.
Let’s put numbers at the nodes and just to illustrate the angle option
I’ll put the numbers slanted at 45 degrees, just because I can. So
here is how I think. I want the characters just above each node, say
5 units. So from node 1, the numer 1 would only have to go a dx=0 and
dy=5 from the node which would require L 0 5 scale 45 1 for the command.
So I’m guessing that the following labels should work using the 96
and 102 offsets.
L 0 5 2 45 1
L 0 -91 2 45 2
L 102 -91 2 45 3
L 102 -43 2 45 4
L 102 5 2 45 5
From this addition I get:
155
156
From this illustration you can see how it’s done. The first pass
will usually wind up close and you can return to the data and fine tune.
For the complete K8IQY PVXO schematic it takes 47mS (that’s milliseconds)
to run both the schematic rendering program and the dxy2ps to generate
the postscript file, so computer time is not an issue for redrawing
during the fine tuning process. You just want to plan ahead as much
as possible on the data to reduce the number of times it takes to get
the schematic correct.
Now here is what I get for the transformer from the original schematic.
Because I don’t have a filled dot character I’ll use a lower case o
with a scale factor of only 1 and when you draw a schematic this will
come close enough to emulate a filled dot.
Here is the result and here is the data.
157
158
200 200 2
T1 1 6 0 T1 ~
# put pen at node 1, upper left of xformer
P1
L 24 60 3 0 T1
L -30 30 2 0 1:4:11T(FT37-43)
L -20 -45 2 0 1T
L 4 -68 1 0 o
L 75 -10 1 0 o
L 75 -45 1 0 o
L 90 -60 2 0 11T
L 90 -25 2 0 4T
e
159
160
17. — Additional Transistor Symbols
Since K8IQY use an NPN with the emitter to the counter clockwise
direction from the collector, I used Q3 for transistor type 3 to call
the function to draw it.
I made up two addition functions for the NPN and PNP with the base,
emitter, collector order in counter clockwise order. Q1 is used to
get the NPN and Q2 is used to generate the PNP. Here is the result.
case ’Q’: /* transistor 1=NPN 2=PNP 3=NPN */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) xistorNPN(x,y,pin,scale,rot);
if( type==2 ) xistorPNP(x,y,pin,scale,rot);
if( type==3 ) transNPN(x,y,pin,scale,rot);
break;
}
161
void transNPN(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-10*scale;yp=-6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for BJT */
x_center=xs+xp;y_center=ys+yp;
/* while at pin 1 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center;ypoint[npoints]=y_center;
xp=7*scale;yp=0*scale;rotate(&xp,&yp,rot); circle(x_center+xp,y_center+yp,6*scale,72);
/* draw line from pin 1 to vertical line */
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw vertical line */
xp=6*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw collector */
xp=6*scale;yp=-2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=-6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 2 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw emitter */
xp=6*scale;yp=2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw arrow for emitter */
xp=7*scale;yp=5*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=9*scale;yp=5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=9*scale;yp=3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=7*scale;yp=5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* move to pin 3 */
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
}
162
void xistorNPN(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-10*scale;yp=-6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for BJT */
x_center=xs+xp;y_center=ys+yp;
/* while at pin 1 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center;ypoint[npoints]=y_center;
xp=7*scale;yp=0*scale;rotate(&xp,&yp,rot); circle(x_center+xp,y_center+yp,6*scale,72);
/* draw line from pin 1 to vertical line */
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw vertical line */
xp=6*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw emitter */
xp=6*scale;yp=-2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=-6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw arrow for emitter */
xp=7*scale;yp=-5*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=9*scale;yp=-5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=9*scale;yp=-3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=7*scale;yp=-5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw collector */
xp=6*scale;yp=2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* move to pin 3 */
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
x=x_center+xp;y=y_center+yp;
}
163
void xistorPNP(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-10*scale;yp=-6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for BJT */
x_center=xs+xp;y_center=ys+yp;
/* while at pin 1 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center;ypoint[npoints]=y_center;
xp=7*scale;yp=0*scale;rotate(&xp,&yp,rot); circle(x_center+xp,y_center+yp,6*scale,72);
/* draw line from pin 1 to vertical line */
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw vertical line */
xp=6*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw collector */
xp=6*scale;yp=-2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=-6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 2 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw emitter */
xp=6*scale;yp=2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw arrow for emitter */
xp=7*scale;yp=-3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=7*scale;yp=-5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=9*scale;yp=-3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=7*scale;yp=-3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* move to pin 3 */
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
}
164
18. — Small Wonder Labs SWL40+ Transceiver
Because of the rewrite of the schematic program from scratch, I now
have a ton of schematic files that will not work with the new program.
I still have the original program, so that is not a problem but I do
want to be able to use one program for everything. So, as part of this
tutorial, we will continue doing different schematics and add components
as we encounter parts that we don’t have a symbol for. This is just
something that you and I will have to do as we mature the program. There
is no rush, so take your time.
On the next page is a schematic of the Dave Benson, K1SWL, Small
Wonder Labs SWL-40+ transceiver. Reduced in size a bit to fit the page.
Print off the page for following along. You can easily do a printing
of one page using most PDF viewers. Just note the page number and invoke
the print page command from the utility or lp -Ppage number schem.pdf
from the command line in Linux.
165
166
The first question that may come to your mind on a large project
like this is "where do I start?". Well, first look at the schematic
and see if there is one continuous flow of connections. No? In this
one there are two main flows. One across the bottom (transmitter) and
one across the top (receiver).
What I’d do and what I will do is start with the DC power to the
regulator and then the VFO (outlined in red on the previous page). Then
continue to the top section left to right. Then come back later and
go through the transmitter section left to right. Don’t worry too much
about the spacing. Do a small section and run the schematic program
to check for errors and layout and continue on in sections. Makes things
a whole lot easier.
We will develop new parts or components for the program as we get
to them. So here goes. Enter data as I do and run your program to
see if it works. Good exercise and learning experience. Don’t goof
off too much between working sessions if you can spare the time to do
this. Use it or lose it. You’ll forget too much if you lay off the
job for weeks or months at a time.
167
Here is the input that created the above.
0 500 2
o 2 180 V+
w 40 0 x c1 1 100 4 270 C102 0.01 g1 4 0 y1 w 60 0
R 3 0 U2 78L05
w 40 0 x w 50 270 x
r2 2 100 4 180 ~ ~ x
L -50 20 2 0 100K
L -50 0 2 0 TUNE w 30 270 g1 4 0
y3 w 20 0 x c1 1 100 4 270 C203 .01 g1 4 0 y1
r1 1 100 4 0 R18~1M ~ x
d3 2 100 4 90 D2 MV1662 g1 4 0 y1
c1 1 100 4 90 C8 82 w 150 0 x
#D2
e
168
The schematic on the previous page is rotated by 90 degrees because
I have dxy2ps set for landscape mode. I have also magnified the original
output by a factor of 1.5 for the time being and will slowly reduce
the factor as the schematic expands. One of the beauties of PostScript
is that no matter what factor you use to magnify the material it still
comes out crisp and clear either in print or for display on a computer
screen or projector. We can see that I have changed the way the schematic
for the SWL40+ begins. That is the beauty of doing schematics. There
are no hard and fast rules for creating them and hopefully my program
and your program are flexible enough to handle anything that comes up.
If not, then we both work on it to add what we need.
I have been using my program for more than a decade and all this
time I required that the first line have xoffset, yoffset and the text
size for input. Let’s make a small mod to the program to remove that
restriction.
• Add a dot (.) command of the form . xoffset yoffset text size and
set xoffset, yoffset and text size to default values of 100, 100
and 2 respectively. This for cases where we forget to input the
data.
This also now allows me to put comments in before the real data
starts for the program.
• Make up a cheat sheet so we don’t have to go back to the program
listing to remember all the parameters for each of the components.
It also helps to have a way of checking for errors.
• Rename source program from the old name to ecad.c for electronic
computer aided design and compile with gcc -o ecad ecad.c -lm
169
Here is the modification to add the dot command in the switch statement.
home(); /* place pen in home position */
x=0;y=0; /* set pen position to home position */
xoffset=100;yoffset=100;text_scale=2; /* defaults */
...............................stuff not shown..............
/* input commands until EOF or e character in input stream */
while( fscanf(fp_input,"%c",&ch) != -1 && ch != ’e’ )
{
switch(ch)
{
case ’.’: /* command line setup for xoffset,yoffset, text_scale */
fscanf(fp_input,"%d%d%d",&xoffset,&yoffset,&text_scale);
case ’D’: /* display/debug node values */
... ... ...
170
Now I’ve add more parts to the SWL40+ and here is what I get. Went
back to portrait mode for a bit to discuss some things.
Here is the data file. Added some comments before the start of actual
data so that in the future, besides the filename I’ll have some notes
to myself that hopefully give me enough to know what I’m doing and where
I am on a project.
# Small Wonder Labs swl40+ transceiver schematic
# Dave Benson, K1SWL
# reproduced with his permission
# schematic by Chuck Adams, K7QO
# September 2007
# filename swl40new
. 0 500 2
o 2 180 V+
w 40 0 x c1 1 100 4 270 C102 ~.01 g1 4 0 y1 w 60 0
R 3 0 U2 78L08
w 40 0 x w 50 270 x
# place variable resistor for tuning
r2 2 100 4 180 ~ ~ x
# label tuning potentiometer
L -60 20 2 0 100K
L -60 0 2 0 TUNE w 30 270 g1 4 0
# go back to wiper of variable resistor
y3 w 20 0 x c1 1 100 4 270 C203 ~.01 g1 4 0 y1
r1 1 100 4 0 R18~1M ~ x
d3 2 100 4 90 D2 MV1662 g1 4 0 y1
c1 1 100 4 90 C8 82 w 190 0 x
c1 1 100 4 270 C6 3300 x w 80 180 c1 1 100 4 270 C7 XX g1 4 0
# go back to junction of C7, L1 and C9
y2 l1 1 100 4 270 L1 ~ g1 4 0
y1 w 80 0 c1 1 100 4 270 C9 10 x c1 1 100 4 270 C10 270 g1 4 0
y1 w 100 0
L 10 10 2 0 to~transmitter
C 1.0 0.0 0.0
x
e
171
And here is the current plot. The red junction point is where the
data input stopped and where the ’pen’ is sitting. At this point I
want to stop and go back to the C8, C6 and C4 junction to finish the
rest of the VFO and then go up and do the receiver.
I run the program one more time with a D2 command as the lst command
so that I can get the node number at the current C8 and C6 node point.
I find in my data and program I get 20 as the node number. So all I
have to do is remove the D2 and start a series with P20 to get me back
to the correct node point. Easy as can be.
You remember at some point earlier in this document starting at page
72 I mentioned the previous scheme of doing labels and how to change
it for this program. Well, wouldn’t you know it. I think I personally
am going back to adding the labeling statements at the end of each component
function. This means more coding, but I have a great deal more control
over where the labels go depending upon orientation and text size relative
to the component size.
If you want me to add a section on how to do this, email me direct
and I’ll take a day to do it. Here is the addition to the capacitor
routine just for the 270 degree rotation. From it you can get an idea
of how to do it.
172
movepen(x,y);
npoints++;xpoint[npoints]=x;ypoint[npoints]=y;
if( rot==270 ) /* component down from (x,y) */
{
x1 = x+2*scale; /* just to the right of the component */
y1 = y+length/2+8*text_scale; /* just above the 1/2 way */
print(x1,y1,0,text_scale,info1);
y1 = y+length/2-14*text_scale; /* just below the 1/2 way */
print(x1,y1,0,text_scale,info2);
}
Here is the new results with the data input the same but with the
addition of lines to do the labeling in the capacitor and variable cap
routines. I will have to made mods to other functions as I encounter
their use later on. It’s more fun that way.
So going back to actually adding components to the schematic I add
the following to the file just before the e.
L 10 10 2 0 to~transmitter
# move to node 20 which is the node junction for C8 and C6
P20 c1 1 120 4 0 C4 2700 x
c1 1 100 4 270 C5 2700 g1 4 0 y1 w 80 0 r1 1 100 4 270 R17 2.2K g1 4 0
# move to node 20 again
173
P20
w 100 90 x r1 1 100 4 180 R16 22K w 50 270 g1 4 0 y2 w 50 0
Q1 1 6 0 Q2 2N4401
D2
e
And I wind up with:
Now this is one point that I have not been able to automate with
any program. The node at the emitter of Q2 is 47 and the node at C4/C5/R17
connection is 35. I could create a command that would connect the two
nodes, but there would be a slope to the line. I want it to be vertically
aligned.
Before, I would change the length of the wire from the base of Q2
to the precious node, i.e. the node to the left which R16 and the wire
from C6/C4 connects to. I’d play with a couple of times, plot and see
if I got it correct. After years of practice I go pretty good at it.
But now for a new approach. I have D1 to color the nodes and D2 to
color the nodes and print the node number above the node. Let’s add
a a printout of the node numbers and the (x,y) coordinates of the points.
So I add the two lines below the previous segment in nodelabels and
I get on the screen when I run ecad with the dataset:
174
/* write node number just to the upper right of the node */
print(xpoint[i]+10,ypoint[i]+10,0,2,info);
/* write node number and coordinates to the screen */
printf(" node number = %d x,y = %d %d \n",i,xpoint[i],ypoint[i]);
node number = 35 x,y = 665 0
node number = 36 x,y = 665 -100
node number = 37 x,y = 665 0
node number = 38 x,y = 745 0
node number = 39 x,y = 745 -100
node number = 40 x,y = 545 0
node number = 41 x,y = 545 100
node number = 42 x,y = 445 100
node number = 43 x,y = 445 50
node number = 44 x,y = 545 100
node number = 45 x,y = 595 100
node number = 46 x,y = 595 100
node number = 47 x,y = 655 64
node number = 48 x,y = 655 136
From this I see that nodes 35 and 47 differ by 10, so I increase
the length of the wire to the base of Q2 by 10. Then draw the vertical
line of length 64, the delta-y value between the two nodes and I get
perfect alignment.
175
This completes the VFO section except for the wire from Q2/R15 that
goes to the power line from D2. I’m using a highlighter on the original
diagram to keep track just so I don’t lose a component or wire. You
should do the same, if possible, for every project done this way.
176
I want to add a dashed line drawing function to the program to allow
me to outline sections. So here it is.
void dashedline(int x1,int y1,int x2,int y2)
{
int line_length, dash_length=10;
int i,j,k,number_segments;
int dx,dy;
line_length = (int) sqrt( (float) (x1-x2)*(x1-x2) + (float) (y1-y2)*(y1-y2) );
number_segments= line_length/dash_length; /* number of dashes and spaces */
dx=(x2-x1)/number_segments;
dy=(y2-y1)/number_segments;
for( i=0 ; i<number_segments ; i++ )
{
if( i%2 == 0 ) movepen(x1+i*dx,y1+i*dy);
if( i%2 == 1 ) line(x1+i*dx,y1+i*dy);
}
line(x2,y2); /* make sure line ends at (x2,y2) */
}
In the main function, I added the command - since we have used up
the letters D and d.
case ’-’: /* dashed line from point (x1,y1) to (x2,y2) */
{
fscanf(fp_input,"%d%d%d%d",&x1,&y1,&x2,&y2);
dashedline(x1,y1,x2,y2);
movepen(x,y);
break;
}
177
178
Or you might prefer blue for the outline. Seems a little less harsh
to me.
179
19. — IC symbol
OK. Finishing off the VFO section we get to the NE602A mixer U1 in
the receiver. We need to create a symbol for this. In a lot of the
literature you will see the mixer drawn with the OSC and MIXER symbols
within the rectangle. That is fine with me, but here I want flexibility
on numbering the pins so that the diagram is easier to layout. So I’m
going to stick with the old symbol. Here it is with the 8 nodes shown
with red dots and numbered in clock-wise order. The numbering is arbitrary
and not set in concrete.
b8
b7
b 4
b 3
b6
b5
b1
b2
b
I have placed a red dot at the origin in order to remind you that
all coordinates are relative to the position. In drawing the rectangle,
all we need do is place the pen at the origin and draw four simple lines.
Then we will mark the 8 pin positions in the node array. That’s all
there is to it. We will just leave the pen at the node 1 position,
because we most likely will use the goto node command to do the rest
of the drawing from each node of the IC.
180
void IC8pin(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int xorigin,yorigin;
if( pin==1 ) {xp=-4*scale;yp=0*scale;rotate(&xp,&yp,rot);}
if( pin==2 ) {xp=-12*scale;yp=0*scale;rotate(&xp,&yp,rot);}
if( pin==3 ) {xp=-16*scale;yp=-3*scale;rotate(&xp,&yp,rot);}
if( pin==4 ) {xp=-16*scale;yp=-9*scale;rotate(&xp,&yp,rot);}
if( pin==5 ) {xp=-12*scale;yp=-12*scale;rotate(&xp,&yp,rot);}
if( pin==6 ) {xp=-4*scale;yp=-12*scale;rotate(&xp,&yp,rot);}
if( pin==7 ) {xp=0*scale;yp=-9*scale;rotate(&xp,&yp,rot);}
if( pin==8 ) {xp=0*scale;yp=-3*scale;rotate(&xp,&yp,rot);}
/* set origin and move pen to 8-pin IC origin */
xorigin=xs+xp;yorigin=ys+yp;movepen(xorigin,yorigin);
xp=16*scale;yp=0*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
xp=16*scale;yp=12*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
xp=0*scale;yp=12*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
movepen(xs,ys); /* move pin back to junction */
/* create the points or nodes for the 8 pins */
xp=4*scale;yp=0*scale;rotate(&xp,&yp,rot); /* pin 1 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=12*scale;yp=0*scale;rotate(&xp,&yp,rot); /* pin 2 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=16*scale;yp=3*scale;rotate(&xp,&yp,rot); /* pin 3 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=16*scale;yp=9*scale;rotate(&xp,&yp,rot); /* pin 4 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=12*scale;yp=12*scale;rotate(&xp,&yp,rot); /* pin 5 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=4*scale;yp=12*scale;rotate(&xp,&yp,rot); /* pin 6 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=0*scale;yp=9*scale;rotate(&xp,&yp,rot); /* pin 7 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=0*scale;yp=3*scale;rotate(&xp,&yp,rot); /* pin 8 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
}
181
And here is the case to invoke the routine. There will be other
IC symbols so we start with the 8-pin IC as type 1.
case ’I’: /* IC component 1=8-pin DIP */
{
fscanf(fp_input,"%d%d%d%d%s%s",&type,&pin,&scale,&rot,&info1,&info2);
if( type==1 ) IC8pin(x,y,pin,scale,rot);
}
182
The lines to get the section for U1 and the immediate surrounding
area gets pretty ugly in some ways. You have to obtain the node numbers
by running the program with the D2 option just before the end. Then
look at the plot and use those numbers to get to the points, especially
for something as complex as an IC which has 8 points to connect to.
I know of no other way to do it and if you have written your program
to this point and have a neat way to do it, by all means let us know.
I’ll put your comments and code here in this tutorial. Thanks
... deleted early stuff already shown ...
P20
w 100 90 x r1 1 100 4 180 R16 22K w 50 270 g1 4 0 y2 w 60 0
Q1 1 6 0 Q2 2N4401
# move to the emitter and draw line to C4/C5/R17 node
P47 w 64 270
# move to the collector of Q2
P48 w 50 90 x
# move to the R18 right node
P44 w 86 90 x r1 1 120 4 0 R15 47K y1
c1 1 100 4 90 C3 10 x w 80 180 c1 1 100 4 270 C2 47 g1 4 0 y2 w 180 90 x
C 0.0 0.0 1.0
- 190 -400 190 320
- 190 -400 800 -400
- 800 -400 800 320
- 190 320 800 320
t 200 -380 3 0
VFO SECTION
C 0.0 0.0 0.0
P61 I1 2 10 0 U1 NE602A
L -5 10 2 0 6
# go to pin 1 of U1 and do label and wire to ground connection
P63 x L -5 10 2 0 3 w 50 270 g1 4 0
P69 L 10 -5 2 0 2
# go to pin 8 of U1
P70 x L 10 -5 2 0 1 w 40 180 x c1 1 56 3 90 ~ ~
# go back to bottom of C1
y1 L -5 -25 2 0 C1 L -10 -50 2 0 150 w 40 180
# now put T1 to the left of U1
T2 3 7 0 T1 ~
P83 w 80 0 x
P84 w 40 0 w 30 270
183
P81 w 40 270 g1 4 0
P80 w 100 180 x L 0 10 2 0 3 r2 2 100 4 180 ~ ~ x L 10 0 2 0 1
L -20 20 2 90 RF~GAIN~~5K w 20 270 g1 4 0
# goto wiper of RF gain control pot
P94 x L 0 10 2 0 2 w 30 0 w 100 270 o 1 270 B L 10 0 2 0 B
e
184
Continuing on drawing the schematic with the program as we have it
set up, we find we are able to get up to the dual op-amp U4. We don’t
have a symbol for an amplifier, so we need to add it to our program.
Here is what the schematic looks like up to this point. Very nice progress
with our work so far.
185
There is one thing about the op-amp symbol. Sometimes we can use
it for a straight amplifier by having one point in and one point out,
so I’ll account for that by adding one point centered on the left hand
side of the symbol. Here it is:
b 8
b 1
b 2b
3
b
4
b 5
b6
b7
As you can see there are a large number of nodes for this part. Rarely
will you need very many, but I have allowed for the most common uses
in schematics that I have seen. Feel free to modify in any way you
want. Note that the nodes along the slopping sides of the symbol are
not exactly on the lines. In order to come up with a diagram where
you are exactly on a line the shape will be off a little and probably
be unnerving to some. Who knows?
So with all that said, here is the opamp routine as I have written
it. The symbol origin is the lower left vertex.
Here is the code section for opamp.
void opamp(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int xorigin,yorigin;
186
if( pin==1 ) {xp=0*scale;yp=-5*scale;rotate(&xp,&yp,rot);}
if( pin==2 ) {xp=0*scale;yp=-2*scale;rotate(&xp,&yp,rot);}
if( pin==3 ) {xp=-2*scale;yp=-1*scale;rotate(&xp,&yp,rot);}
if( pin==4 ) {xp=-7*scale;yp=-4*scale;rotate(&xp,&yp,rot);}
if( pin==5 ) {xp=-9*scale;yp=-5*scale;rotate(&xp,&yp,rot);}
if( pin==6 ) {xp=-7*scale;yp=-6*scale;rotate(&xp,&yp,rot);}
if( pin==7 ) {xp=-2*scale;yp=-9*scale;rotate(&xp,&yp,rot);}
if( pin==8 ) {xp=0*scale;yp=-8*scale;rotate(&xp,&yp,rot);}
/* set origin and move pen to 8-pin IC origin */
xorigin=xs+xp;yorigin=ys+yp;movepen(xorigin,yorigin);
xp=9*scale;yp=5*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
xp=0*scale;yp=10*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot);line(xorigin+xp,yorigin+yp);
movepen(xs,ys); /* move pin back to junction */
xp=0*scale;yp=5*scale;rotate(&xp,&yp,rot); /* pin 1 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=0*scale;yp=2*scale;rotate(&xp,&yp,rot); /* pin 2 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=2*scale;yp=1*scale;rotate(&xp,&yp,rot); /* pin 3 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=7*scale;yp=4*scale;rotate(&xp,&yp,rot); /* pin 4 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=9*scale;yp=5*scale;rotate(&xp,&yp,rot); /* pin 5 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=7*scale;yp=6*scale;rotate(&xp,&yp,rot); /* pin 6 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=2*scale;yp=9*scale;rotate(&xp,&yp,rot); /* pin 7 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
xp=0*scale;yp=8*scale;rotate(&xp,&yp,rot); /* pin 8 */
npoints++;xpoint[npoints]=xorigin+xp;ypoint[npoints]=yorigin+yp;
print(xorigin+2*scale,yorigin+5*scale-2*text_scale,rot,text_scale,info1);
}
187
With the addition of the op-amp we now have everything we need for
completion of the receiver portion of the schematic. This will be a
learning experience for laying out a schematic, so take your time in
completing this. Just take a couple of parts at a time and rerun the
program to see how it is going. This is not something I can do for
you step-by-step. It just wouldn’t be fair.
The next page has the schematic for the completion of the receiver.
Looking at it closely I have a mod to make later on. The C25/R13 combo
is going on the bottom instead of the top and R11/R12/C107 along with
R6 and C106 are going on the top. This way allows me to connect the
+12V and +8V lines across the top instead of having them terminate with
connector symbols.
188
At this point in the transmitter section we run into T3 which is
also a transformer like T2, but a reflection about the Y axis. Instead
of getting fancy and putting in a reflection variable, I’ll just write
another function xformer03 and draw the transformer as shown below.
And on the following page is the function xformer03 which will reproduce
the above with 4 nodes, each at the ends of the windings.
190
void xformer03(int xs,int ys,int pin,int scale,int rot)
{
int x1,y1,xpin,ypin;
if( pin==1 ) {x1=0;y1=0;}
if( pin==2 ) {x1=0*scale;y1=6*scale;}
if( pin==3 ) {x1=-14*scale;y1=6*scale;}
if( pin==4 ) {x1=-14*scale;y1=-6*scale;}
rotate(&x1,&y1,rot);
xpin=xs+x1;ypin=ys+y1;
x1=0*scale;y1=0*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 1 */
x1=4*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-1*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=2*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-3*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=5*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=5*scale;y1=-5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=4*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=0*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 2 */
/* now draw two vertical lines for transformer core */
x1=6*scale;y1=-6*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=6*scale;y1=6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
print(xpin+x1,ypin+y1+10,0,text_scale,info1);
x1=8*scale;y1=-6*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
x1=8*scale;y1=6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
/* now draw secondary windings */
x1=14*scale;y1=-6*scale;rotate(&x1,&y1,rot); movepen(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 3 */
x1=14*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=-3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=-1*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=0*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=1*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=2*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=12*scale;y1=3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
191
x1=10*scale;y1=3*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=4*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=9*scale;y1=5*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=10*scale;y1=6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
x1=14*scale;y1=6*scale;rotate(&x1,&y1,rot); line(xpin+x1,ypin+y1);
npoints++;xpoint[npoints]=xpin+x1;ypoint[npoints]=ypin+y1; /* pin 4 */
x=xpin+x1;y=ypin+y1;movepen(x,y);
}
192
After adding the previous routine for the transformer the only thing
missing is a PNP transistor with the emitter and the collector switched.
Here is the routine for that.
void transPNP(int xs,int ys,int pin,int scale,int rot)
{
int xp,yp;
int x_center,y_center;
if( pin==1 ) {xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==2 ) {xp=-10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
if( pin==3 ) {xp=-10*scale;yp=-6*scale;rotate(&xp,&yp,rot); movepen(xs+xp,ys+yp);}
/* congratulations, you are now at new point for BJT */
x_center=xs+xp;y_center=ys+yp;
/* while at pin 1 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center;ypoint[npoints]=y_center;
xp=7*scale;yp=0*scale;rotate(&xp,&yp,rot); circle(x_center+xp,y_center+yp,6*scale,72);
/* draw line from pin 1 to vertical line */
xp=0*scale;yp=0*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=0*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw vertical line */
xp=6*scale;yp=-4*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=6*scale;yp=4*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* draw collector */
xp=6*scale;yp=-2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=-6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 2 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw emitter */
xp=6*scale;yp=2*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* while at pin 3 create a node point, even though it may be redundant */
npoints++;xpoint[npoints]=x_center+xp;ypoint[npoints]=y_center+yp;
/* draw arrow for emitter */
xp=7*scale;yp=3*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
xp=7*scale;yp=5*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=9*scale;yp=3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
xp=7*scale;yp=3*scale;rotate(&xp,&yp,rot); line(x_center+xp,y_center+yp);
/* move to pin 3 */
xp=10*scale;yp=6*scale;rotate(&xp,&yp,rot); movepen(x_center+xp,y_center+yp);
}
Now with everything in place I was able to finish the schematic.
I won’t kid you, this took some time and I’m not through. I didn’t
have the labeling for the transistors fixed and the inductor labeling
needs work, but I’ll do that next.
193
OK. This was a very difficult project. So if you feel frustrated
and want to give it up, please please don’t do it. Look at
http://www.commspeed.net/k7qo/k7qonotebook.pdf
and if it’s not there, keep looking. I’m going to start with some simple
circuits and go from there. I’ll be adding a few more functions to
the code, so stay tuned.
I’ll try to come back here to add them, but for now I’m about done
with this 200 page monster. Thanks for reading and hopefully you did
the work.
If you compare the new layout with the previous result on page 166,
you can hopefully see some significant improvements. Now I have the
obligation and duty to go back and redo everything I have ever done
over the past 20 years using the old program. This should be interesting,
so watch for the results in the K7QO QRP Notebook, modeled after W1FB’s
QRP Notebook.
dit dit
194
DXY2PS Man Pages
NAME
dxy2ps --- A Roland DXY plot command filter to PostScript
SYNOPSIS
dxy2ps -amr [-l line sizes] [-s scale] [-x offset] [-y offset] [plotfile]
DESCRIPTION
This filter is used to convert the Roland DXY and the Roland Graphics
Language (RD-GL) (which is a superset of the Hewlet Packard Graphics
Language (HP-GL)) commands to PostScript.
The RD-GL commands are only operated on if each command is prefixed
with a "V
", therfore if your plotfile only contains HP-GL or RD-GL commands
use the filter hpgl2ps.
The default conditions for dxy2ps are:
1. The plot translation is from ISO A3 (420mm x 297mm) to ISO A4
(297mm x 210mm) on the LaserWriter.
2. Line thicknesses are in millimeters and are for lines (pens)
1 to 9: ( 0.1, 0.2, 0.3, 0.4, 0.5, 0.7, 1.0, 1.25, 1.5 )
Note: If the plotfile is not specified then standard input is assumed.
ARGUMENTS
-a
0.3cm Draw on an A4 ISO (297mm x 210mm) sheet of paper. This will
give an approximate 1:1 translation to the LaserWriter.
-l line sizes
This option allows the default line sizes to be replaced with different
sizes starting from line (pen) one through nine. The new sizes (real
or integer) are entered in a comma separated string (line size) and
are in millimeters. Only the sizes entered in the string will be changed
with respect to lines (pens) one to nine while nonentered line (pen)
sizes will retain the default sizes.
In the following example only the first three sizes will be changed.
eg: dxy2ps -l 0.4,0.3,1 file | lpr -Plaser1
196
-m
Enable the manual feed on the LaserWriter (cutoff time 3 minutes).
-r Rotate plot(s) through 90 degrees. (The plot is made smaller)
-s scale
Scale the plot from 0.1 to 3 times its original size.
-x offset
Place an X offset (in mm) in the plot.
-y offset
Place an Y offset (in mm) in the plot.
Note: Offsets are with respect to the 0,0 (origin) of the HP-GL
/ RD-GL plotting commands, thus if the minimum plotting coordinates
are below either the X and/or Y zero axis then a positive offset is
applied. Similarly if the minimum plot coordinates are above the X
and/or Y zero axis then a negative offset is applied.
FILES
/usr/local/bin/dxy2ps
/usr/local/bin/hpgl2ps
AUTHOR
Don McCormick
CSIRO
Division of Applied Physics
PO 218, Lindfield, N.S.W., 2070
Australia
BUGS
Some of the graphics commands are unimplemented and a warning will
be displayed. If this command is vital then it must be written into
the code.
No interactive command is suported.
If any bugs are found notify damc@natmlab or damc@nifty or root.
197
Recommended