[McHUG] Re: McHUG Digest, Vol 6, Issue 2
J Sears
aat3ok at yahoo.com
Mon Oct 13 12:29:59 EDT 2008
Steve,
As you may have found out already.. copying hand sent CW can be problematic at best. Many years ago I played around with this problem.. and what I learned is there is a poblem because of the varying speed of the sender. The way to cope with this is load the incoming characters into a buffer (say 25 characters) and clock them to determine speed of EACH character. You then have to apply the calculated speed to that particular charcter as it is decoded. I lost intereste in the project and never got around to finishing but I am convinced this is the way to go.
Jim WA3MEJ
--- On Sun, 10/12/08, mchug-request at mailman.qth.net <mchug-request at mailman.qth.net> wrote:
From: mchug-request at mailman.qth.net <mchug-request at mailman.qth.net>
Subject: McHUG Digest, Vol 6, Issue 2
To: mchug at mailman.qth.net
Date: Sunday, October 12, 2008, 4:01 AM
Send McHUG mailing list submissions to
mchug at mailman.qth.net
To subscribe or unsubscribe via the World Wide Web, visit
http://mailman.qth.net/mailman/listinfo/mchug
or, via email, send a message with subject or body 'help' to
mchug-request at mailman.qth.net
You can reach the person managing the list at
mchug-owner at mailman.qth.net
When replying, please edit your Subject line so it is more specific
than "Re: Contents of McHUG digest..."
Today's Topics:
1. CW Decoder for Arduino (n3sb at qis.net)
----------------------------------------------------------------------
Message: 1
Date: Sat, 11 Oct 2008 20:56:05 -0400
From: n3sb at qis.net
Subject: [McHUG] CW Decoder for Arduino
To: mchug at mailman.qth.net
Message-ID: <1223772965.48f14b2564fce at webmail.qis.net>
Content-Type: text/plain; charset=ISO-8859-1
Hi Folks;
The code below is my very first attempt at decoding CW. It works, but needs
refinement. It's attached below for anyone who wants to play with it.
73; Steve, N3SB
-------------------------------------------------------------------
// Program morse1 written October 11, 2008 N3SB
//
// This program is the first attempt to receive CW on an Arduino (ATMega168
based)
// The program depends on a 1000 Hz interrupt routine, which is used to read
the
status of the "key" line.
// From there the length of the CW elements is measured, and the elements are
translated to ASCII characters.
// #include <avr/interrupt.h>
// #include <avr/io.h>
#include <avr/pgmspace.h>
// Macro and other defines
#define KEY_UP 1 // Logic level expected when key is up
#define KEY_DOWN 0 // Logic level expected when key is down
#define INIT_TIMER_COUNT 6
#define RESET_TIMER2 TCNT2 = INIT_TIMER_COUNT
#define UNKNOWN Serial.print("_")
// Variable defines
unsigned int int_counter = 0;
unsigned int key_len = 0;
unsigned char edge = 0;
unsigned char key_up_edge = KEY_UP * 15; // set 4 LSBs to the value of
KEY_UP
unsigned char key_down_edge = KEY_DOWN * 15; // set 4 LSBs to the value of
KEY_DOWN
unsigned char key_status = KEY_UP;
unsigned char dot_len = 100; // start with a 20 ms dot length
unsigned int cw_char = 0; // holds the character we are
receiving
unsigned int char_length = 0; // counts the number of elements
in the character
unsigned char space = 0; // flag for tracking the output
of
spaces
unsigned int q;
// Pin Defines
int ledpin = 13; // Arduino pin used by the LED
int keypin = 2; // Arduino pin used for CW input
// Aruino runs at 16 Mhz, so we have 1000 Overflows per second...
// 1 / ( (16000000 / 64) / 256) = 1 / 1000
ISR(TIMER2_OVF_vect) {
RESET_TIMER2;
if (int_counter < 1000) { // Keep int_counter from overflowing
int_counter++;
}
edge = (edge << 1) | digitalRead(keypin); // Shift in the status of
the key
pin
// hack
// digitalWrite(ledpin, digitalRead(keypin)); // turn off LED
} // end of timer interrupt routine
void setup() {
pinMode(keypin, INPUT); // configure key pin for input
// hack to enable the internal pull-up on the keypin
PORTD = PORTD | 4;
pinMode(ledpin, OUTPUT); // configure LED pin for output
Serial.begin(9600);
Serial.println("Program MorseRX1 - 11 October 2008 - N3SB");
Serial.println("Initializing 1 ms timer interrupt");
//Timer2 Settings: Timer Prescaler /64,
TCCR2B |= (1<<CS22); // turn on CS22 bit
TCCR2B &= ~((1<<CS21) | (1<<CS20)); // turn off CS21 and
CS20 bits
// Use normal mode
TCCR2A &= ~((1<<WGM21) | (1<<WGM20)); // turn off WGM21
and WGM20 bits
TCCR2B &= ~(1<<WGM22); // turn off WGM22
// Use internal clock - external clock not used in Arduino
ASSR |= (0<<AS2);
//Timer2 Overflow Interrupt Enable
TIMSK2 |= (1<<TOIE2) | (0<<OCIE2A); //Timer2 Overflow
Interrupt Enable
RESET_TIMER2;
digitalWrite(ledpin, LOW); // turn off LED
sei();
} // end of Arduino setup structure
void loop() {
if (key_status == KEY_UP)
{
// look for a stable transition to the KEY_DOWN state
if ( (edge & 15) == key_down_edge ) // Key status has transitioned to
DOWN
{
key_status = KEY_DOWN; // Set new key status
int_counter = 0; // clear int counter
// Serial.println("KEY_DOWN Edge detected"); // hack
}
else
{
// In this state the key is up. We now time how long it's up to
determine
inter-element, inter-character, and inter-word spacing
// Inter-element is one dot long
// Inter-character is three dots long
// Inter-word is seven dots long
// The code below currently only looks for inter-character and inter-word
spacings
digitalWrite(ledpin, LOW); // turn off LED
// hack - the threshold should be dot_len * 2, but 3 works much better
for
some reason.
if (int_counter > (unsigned int)(dot_len * 3) ) // if the
inter-element
spacing is greater than our dot length * 2 then its a character separator.
{
// Now we get to process a character - but only if one has been
received!
if (char_length > 0)
{
process_character( );
cw_char = 0;
char_length = 0;
space = 0;
}
}
if (int_counter >= (dot_len * 6) ) // inter-word spacing has been
detected - or a long pause
{
// print a space - but only one!
if (space == 0)
{
Serial.print(" ");
space = 1;
}
}
}
}
else // Key status is not KEY_UP (it's
down)
{
// look for a stable transition to the KEY_UP state
if ( (edge & 15) == key_up_edge ) // Key status has transitioned to
UP
{
key_status = KEY_UP; // Set new key status
key_len = int_counter; // Save length of time key was down
int_counter = 0; // clear int counter
// Serial.println("KEY_UP Edge detected"); // hack
// Let's now see how long the key was down - was it a dot or a dash?
if (key_len < (dot_len << 1) ) // threshold for dot - dash
evaluation
is dot length * 2
{
// Guessing it's a dot, or something shorter....
if (key_len > (dot_len >> 1) ) // a valid dot has to be at
least half
as long as our dot length
{
// It's a good dot!
cw_char = cw_char << 1; // shift the character storage
left (and
save a 0 for the dot)
char_length++; // increment the character length
counter
}
}
else
{
// Guessing it's a dash, or something longer....
if (key_len < (dot_len << 3) ) // a vaild dash has to be no
longer
than our dot length * 8
{
// It's a good dash!
cw_char = (cw_char << 1) | 1; // shift the character storage
left
(and save a 1 for the dash)
char_length++; // increment the character length
counter
}
}
} // end of key up edge detection
// In this state the key is down. We currently don't do anything more than
wait
for the key to go back up!
digitalWrite(ledpin, HIGH); // turn on LED
}
} // end of Arduino loop structure
void process_character(void)
{
// This function uses the global variable cw_char and char_length to
translate
a bit pattern into its ASCII equivalent
// The pattern is represented by bits stored in cw_char. 0 represents a dot
and 1 represents a dash.
// The LSB of cw_char has the last element received. char_length contains the
number of elements.
// cw_char is a 16 bit variable, which allows for the translation of
extra-long characters. Some folks run CQ together, for example.
// The conversion process could have been done with a look-up table, but this
approach allows us to do some other things based
// on what character was received.
// 0123456789
/*
*/
switch (char_length) // evaluate the bit patter based on the number of
elements
{
case 1: // one element characters - E T
switch (cw_char)
{
case 0:
Serial.print("E");
break;
case 1:
Serial.print("T");
break;
default:
UNKNOWN; // this condition should never occur
}
break;
case 2: // two element characters I A N M
switch (cw_char)
{
case 0:
Serial.print("I");
break;
case 1:
Serial.print("A");
break;
case 2:
Serial.print("N");
break;
case 3:
Serial.print("M");
break;
default:
UNKNOWN; // this condition should never occur
}
break;
case 3: // three element characters S U R W D K G O
switch (cw_char)
{
case 0:
Serial.print("S");
break;
case 1:
Serial.print("U");
break;
case 2:
Serial.print("R");
break;
case 3:
Serial.print("W");
break;
case 4:
Serial.print("D");
break;
case 5:
Serial.print("K");
break;
case 6:
Serial.print("G");
break;
case 7:
Serial.print("O");
break;
default:
UNKNOWN; // this condition should never occur
}
break;
case 4: // four element characters H V F _ L _ P J B X C Y Z
Q _ _
switch (cw_char)
{
case 0:
Serial.print("H");
break;
case 1:
Serial.print("V");
break;
case 2:
Serial.print("F");
break;
case 3:
UNKNOWN;
break;
case 4:
Serial.print("L");
break;
case 5:
UNKNOWN;
break;
case 6:
Serial.print("P");
break;
case 7:
Serial.print("J");
break;
case 8:
Serial.print("B");
break;
case 9:
Serial.print("X");
break;
case 10:
Serial.print("C");
break;
case 11:
Serial.print("Y");
break;
case 12:
Serial.print("Z");
break;
case 13:
Serial.print("Q");
break;
case 14:
UNKNOWN;
break;
case 15:
UNKNOWN;
break;
default:
UNKNOWN; // We have tested for all possible combinations.
This
condition should never occur
}
break;
case 5: // five element characters 5 4 3 2 as ar 1 6 bt / kn
7 8 9 0 (and a bunch of invalid characters in between)
switch (cw_char)
{
case 0:
Serial.print("5");
break;
case 1:
Serial.print("4");
break;
case 3:
Serial.print("3");
break;
case 7:
Serial.print("2");
break;
case 8:
Serial.print("as");
break;
case 10:
Serial.print("ar");
break;
case 15:
Serial.print("1");
break;
case 16:
Serial.print("6");
break;
case 17:
Serial.print("bt");
break;
case 18:
Serial.print("/");
break;
case 22:
Serial.print("kn");
break;
case 24:
Serial.print("7");
break;
case 28:
Serial.print("8");
break;
case 30:
Serial.print("9");
break;
case 31:
Serial.print("0");
break;
default:
UNKNOWN; // A number of patterns have not been specifically
tested. This condition can occur.
}
break;
case 6: // six element characters (punctuation marks) ?
period @ comma
switch (cw_char)
{
case 12:
Serial.print("?");
break;
case 21:
Serial.print(".");
break;
case 26:
Serial.print("@");
break;
case 51:
Serial.print(",");
break;
default:
UNKNOWN; // Most of the patterns have not been specifically
tested. This condition can occur.
}
break;
case 7:
break;
default:
UNKNOWN; // unknown or undefined character.
}
}
------------------------------
_______________________________________________
McHUG mailing list
McHUG at mailman.qth.net
http://mailman.qth.net/mailman/listinfo/mchug
End of McHUG Digest, Vol 6, Issue 2
***********************************
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://mailman.qth.net/pipermail/mchug/attachments/20081013/02631b51/attachment-0001.htm
More information about the McHUG
mailing list