Some
people like to claim that CW was the first digital communications
mode. Others dispute that claim and say it isn't, citing various
reasons. In this section, I'll take a hard look at CW and decide
whether or not it really is a digital mode. In the process I'll also
take a deeper look at the data structure of CW. I hope you aren't
bored to death!

So What IS
Digital?

One definition
that I found of the term "digital" is as follows:
"Representation of quantities in discrete (quantized) units.
A digital system is one in which the information is contained and
manipulated as a series of discrete numbers, as opposed to an analog
system, in which the information is represented as a continuous trace
or curve of the quantity constituting the signal." Well, CW
is sent by either turning a carrier on or off, which is 2 states, so
it qualifies as digital by that definition.

But things are
never quite as simple as they seem! Here's another definition that I
found: "A description of data that is stored or transmitted
as a sequence of ones and zeros. Most commonly, this means binary
data represented using electronic or electromagnetic signals."
So by that definition, CW wouldn't be considered digital
unless it is also binary.

At this point
I can take 2 approaches. One is just to accept the first definition
and say that CW is digital since it involves representing things in
discreet states rather than continuous. The other is to figure out
whether CW can be represented in binary with 1's and 0's or not. If
it can, then it is also digital by the second definition. I'll take
the second approach, since I prefer a little bit of rigor here and
there.

It turns out
that it is fairly easy to define CW and Morse code in terms of a
binary code that can be implemented. And if I can implement it in
binary, then it must be binary. In mathematical terms that would be
known as a "proof by construction" - if we show exactly how
to do something, then it must be true. So where do we start with CW?

First, let's
define a machine, mechanical or electrical, that generates a 1 or 0
level signal of duration δt at each time increment and further
specify that the machine can do nothing else. During each time
increment δt it generates a pulse or it doesn't. Obviously we
can control such a machine with a string of binary 1's and 0's fed to
it at the proper times. Further, let's define the time increments as
1.2/WPM seconds, so each pulse would be equal in length to a Morse
dit. I think we would all agree that I just defined a digital, binary
machine by whatever definition we might choose. So now we must show
that it is possible to use this machine to generate Morse code.

If I want to
use such a machine to send CW I can send a 10 to form a dit and an
element space and a 1110 to form a dah and an element space. After an
element I can send 00 to make a space between characters and after a
word I can send a 00000 to generate the space between words. So that
means I can send various sequences of 1's and 0's to string together
dits and dahs and spaces in any combination that I want. Note that
not all possible sequences are valid for Morse, since combinations
like 0110 should never occur. But that isn't important, the important
thing is that I can easily represent strings of Morse symbols in a
binary code and feed those strings to my ideal machine and it will
generate perfect Morse code.

So is Morse
code and CW digital? You bet it is, by either definition!

So How Can a
Computer Send CW?

That is almost
an easy question, since I just described a binary machine that can
send CW. The trick is that we need to figure out how to generate the
correct sequence of 1's and 0's. It should be simple to make a lookup
table that takes any letter or symbol and returns the correct
sequence. For example, "A" would give 101110, "B"
would give 1110101010, etc. In fact I can even simplify it a little
more and say that the table only contains a 0 for a dit and a 1 for a
dah, or vice versa. So that way "A" would be 01 and "B"
would be 1000, etc. The software then takes the letter or symbol
(probably as an ASCII code), retrieves the table entry and
substitutes 10 for every 0 and 1110 for every 1 in the table code.
Nice compact, easily programmed - and it almost works.

The problem
you'll soon discover if you try to take that approach is that
computer memory and most programing languages store data in fixed
size chunks that are normally considered to be 8 or 16 or some
multiple of 8 bits wide. So if we store 01 for "A" in an
8-bit memory it might look like 00000001 or like 01000000 or
something and there's no way for us to tell what is really being
stored. We could always store the table as "strings," but
that uses 8 bits for each 0 or 1, and doesn't seem too efficient.

The solution I
usually use is to store the data with a 1 added on the front, so "A"
becomes 101, "B" becomes 11000, etc. Now we can save those
in an fixed bit sized register as "A" = 00000101 = 5, "B"
= 00011000 = 24, etc. When I'm ready to send CW, I look up the code
based on the ASCII character, start counting from the left side of
the bit string until I get to a 1, then send either 10 for a 0 or
1110 for a 1 until I get to the end, then send 00 to form the space
at the end of a letter. The only exception is for the "space",
which simply requires sending 00000 and can be handled as an
exceptional case.

Well, almost.
If you're working with 16 bit computer memory or normal integers,
that works just fine. But if you are limited to 8-bit memory or want
to use 8-bit numbers to save space, you run into the inevitable
exception. The longest CW sequences are things like "period"
= 1010101, "comma" = 1110011, etc. Those all fit into 7
bits. However, there is the official code for "error" which
is 8 dits, so with the leading 1 it would require 9 bits to store.
The solution is either to never make mistakes or to handle it as
another special case.

So now that
I've shown how to generate perfect CW in a binary machine, can there
be any doubt left that Morse code and CW is really binary?

And Then
There Are Soundcards

In case you
were wondering, no, you can't just send 0's and 1's as described
above directly to a soundcard to generate CW. If you tried, all you'd
hear is some sort of weak buzzing sound bearing no resemblance to CW.
(No, I'll never admit whether or not I tried it!) To generate CW
using a PC sound card you have to generate the complete audio signal
waveform, so that's a little more troublesome. But since you might be
interested if you've read this far, here's how you do it.

I'm not going
to get into actual soundcard interrupts, etc. You'll have to figure
that out on your own or find one of the soundcard handling widgets,
components, controls, etc. But you do need to understand how a common
PC soundcard works for output. Essentially the soundcard output is a
"digital to analog converter" (DAC). You feed it a number
and it outputs a voltage. And it does that at a specified time
increment that depends on the sample rate. Most soundcards have a
16-bit DAC, which means you give it a 16-bit number and it generates
a voltage corresponding to that number. The 16-bits is nice, since
most computers represent integers as 16-bit numbers, so we can
represent all of the signals that can be sent to a soundcard as a
string of integers inside the PC. But the soundcard (or the soundcard
software drivers) take care of sending the integers at the right
times, so all we need to do is fill a "buffer" with the
integers that we want sent. You can think of a buffer as an array of
memory, if you're not familiar with the term.

In practice
most soundcards handle stereo, so they actually have 2 DAC's, and the
integers representing the voltages for each channel are interleaved,
so the first integer is the right channel, the 2nd the left channel,
the 3rd the right channel at the next time increment, etc. But the
driver allows us to use mono-sound, too, so for mono we only need one
string of integers. In addition, most soundcards operate at some
standard sampling rates. Most handle up to 44kHz sampling, which is
equivalent to audio CD quality sound. OK, it's really 44,200 Hz if
you want to get precise. Most can also handle lower rates for 22,100
and 11,050 Hz just fine, too. There are other higher quality cards
that handle high sample rates, use more than 16 bits, etc., but I'll
just talk about the standard soundcard here.

What if you
want to save the sound in a WAVE file? No problem! You can look up
the specifications for a WAVE file to get the exact format, but
essentially you just save the string of integers to a file with
appropriate header information to define the sample rate, number of
channels, number of bits per sample, the file size, etc.

So that aside,
here's the problem: We have a string of 1's and 0's and we need to
send a pulse every time period there's a 1 and send silence every
time period there's a 0. We need to know 3 basic things. First, what
is the audio frequency of the CW tone, what code speed are we trying
to send and what sample rate that the soundcard is set for.

Let's start
with the soundcard. The time increment is equal to 1/S, where S is
the sample rate in Hz. So for a 44,200 Hz sample rate, each sample
occurs at intervals of 1/44200 = 0.000022624 sec. So we can start
with 0 and for every sample we generate, add 1/44200 seconds to find
the elapsed time. That's easy enough.

Next lets look
at the audio frequency. The time for 1 wavelength is 1/F where F is
the audio frequency in Hz, so for a 800 Hz tone, the audio sound wave
takes 1/800 = 0.00125 sec. That also means that for every audio
cycle, we will need to generate 44200/800 = 55.25 samples, or integer
numbers.

Finally, lets
think about the element time, the length of a dit. We already know
that the dit-length is 1.2/W where W is the code speed in words per
minute. That means that a dit comprises (1.2 F/W) audio cycles and
will occupy (1.2 S/W) audio samples. So for 15 WPM CW at a frequency
of 800 Hz and a 44200 Hz sample rate, a dit will occupy 0.08 sec, be
comprised of 64 cycles and take up 3536 audio samples. So now we to
generate those audio samples.

There are, as
in most things, various way to generate CW on a computer. First, we
know that a perfect audio tone is a sine wave and its cycle time is T
= 1/F seconds. So the equation of the sine wave is V = A sin(t/T),
where A is the amplitude - we'll get to that later. We also know that
we need to generate samples at the sample rate, S, and that each
sample occurs every 1/S seconds. I prefer to define a "numerical
oscillator" to take care of the audio tone generation. In
pseudo-code that would be something like the following, where
AudioDT, SampleDT and Phase are static
variables. Note that I can call ResetOsc to set the
oscillator up, then just call Osc every time I need an
oscillator sample.

function Osc : real; begin Osc:=
sin(Phase); Phase:= Phase +
TwoPi*SampleDT/AudioDT; if (Phase > TwoPi) then
Phase:= Phase - TwoPi; end;

With that
little bit of code out of the way, now all I need to do is generate
enough samples to form a dit and dah. Spaces are easy, they are just
a string of 0's indicating no signal. My approach is to create an
integer array that represents a dit and a dah and an element space.
If I have those predefined, all I need to do is copy them to the
sound buffer any time I need to send an element. One way to do define
those arrays would be as follows in pseudo-code. The same holds for
the dah and for the space, except the dah is 3 times longer and the
space is all 0's, so no need to call the oscillator.

begin dt:=
0; ResetOsc(Tone, SampleRate); i:=
0; while (dt < DitTime)
do begin ditArray[i]:=
A*Osc; dt:= dt +
SampleDT; Inc(i); end; end;

The only
problem with that simple bit of code becomes apparent when you
implement it and listen to it. What we have done is perfectly
modulate the audio tone with a square wave, so extreme clicking is
obvious and the key-clicks extend over a large bandwidth. What we
need to do is filter out the key-clicks to make this into a usable CW
signal.

There is a
large body of literature on how to do DSP filtering, but in this case
it's actually easier to generate a signal that needs no filtering
instead of trying to filter the key-clicks out. In an old and dusty
trigonometry book I found the following relation, which holds for any
angles:

sin(X)
sin(Y) = [cos(X-Y) - cos(X+Y)]/2

What that says
is that if we multiply two sine waves (i.e. sinusoidal signals)
together, we end up with a the sum of 2 sine waves occupying a
bandwidth of twice the difference between them, because (X+Y)-(X-Y) =
2Y. The only "trick" might be to recognize that the signals
are now cosines rather than sines, but since we don't care about the
absolute phase it makes no difference. In other words if we take our
audio sine wave and multiply it by a sine wave that represents the
width of a dit and the space following the dit, we generate the
narrowest possible CW signal with absolutely no key clicks. With this
modification the pseudo-code becomes:

begin dt:=
0; ResetOsc(Tone, SampleRate); i:=
0; while (dt < DitTime)
do begin ditArray[i]:=
A*sin(Pi*dt/DitTime)*Osc; dt:= dt +
SampleDT; Inc(i); end; end;

Of course you
can do the same thing with the dah pulse, except only the first and
last part should be "shaped" using something like the
following code:

if (dt <
DitTime/2) then dahArray[i]:=
A*sin(Pi*dt/DitTime)*Osc; else if (dt > (5*DitTime/6)) then
dahArray[i]:= A*sin(Pi*(5*DitTime/6 - dt)/DitTime)*Osc else
dahArray[i]:= A*Osc;

The only thing
I haven't mentioned is what to use for the signal amplitude, A.
Actually that's pretty easy. The maximum signal amplitude has to fit
into the soundcard DAC's, so we need to use a value less than that
specified by the soundcard. For a typical 16-bit soundcard, the
maximum integer magnitude it can handle is +/-32767, so anything less
than that will work. Larger values will give a louder sound volume
while smaller values yield less volume. I normally use 32000 just to
make sure I don't get close to the edge of the soundcard specs, but
there's no real good reason for that particular value.

So now you
probably know more about computer generating CW than you ever wanted
to know, right?

However, I
think the above discussion has shown that as a binary mode, CW is
pretty simple to implement. All the problems come in when we're
trying to turn it back into an analog signal!