/* Teach Morse Code Copyright (C) 1978, 1984 Howard Cunningham */ /* Revision History 04/18/78 Initial version adapted from 8080 07/31/84 Translated to C */ /* Reference "A Fully Automatic Morse Code Teaching Machine", QST (May 1977) ARRL, Newington, Conn. */ int line = 62; /* terminal line length */ int maxnum = 36; /* maximum alphabet size */ int good = 0; /* error rate bounds */ int bad = 255; int coderadix = 10; /* morse packing interval */ int letter; /* index of selected letter */ int told; /* student told answer flag */ int correct; /* correct response flag */ int num; /* size of current alphabet */ int give; /* response wait time */ int column; /* remain print positions */ int sent; /* realtime letter sent */ /* character code and error rate tables */ int ascii[36]; int error[36]; long morse[36]; /* Interface subroutine to read the system realtime clock. Returns elapsed number of clock ticks modulo a power of two. */ extern int realtime(); /* Interface subroutine to a remote audio oscillator. Subroutine is called only when a change in state is required (i.e. sidetone generation is external). */ extern void buzzer (switch); /* Interface subroutine to sense a user keyboard response. If true is returned, getchar() should advance to the next character without delay. */ extern int response(); /* Interface function to random number generator. Returns 0 < random < 2**15 with even distribution. */ extern int random; /* Initialize tables and control variables to a state approprate for the beginning of a lesson. */ preset () { int i; num = 4; /* minimum alphabet */ give = 3000; /* idle response */ column = 0; /* print positions */ for (i = 1; i <= maxnum; i++) error [i] = bad; error [0] = bad * 30 / 100; ascii [00] = '*'; ascii [01] = 'Q'; morse [01] = 11011; ascii [02] = '7'; morse [02] = 100011; ascii [03] = 'Z'; morse [03] = 10011; ascii [04] = 'G'; morse [04] = 1011; ascii [05] = '0'; morse [05] = 111111; ascii [06] = '9'; morse [06] = 101111; ascii [07] = '8'; morse [07] = 100111; ascii [08] = 'O'; morse [08] = 1111; ascii [09] = '1'; morse [09] = 111110; ascii [10] = 'J'; morse [10] = 11110; ascii [11] = 'P'; morse [11] = 10110; ascii [12] = 'W'; morse [12] = 1110; ascii [13] = 'L'; morse [13] = 10010; ascii [14] = 'R'; morse [14] = 1010; ascii [15] = 'A'; morse [15] = 110; ascii [16] = 'M'; morse [16] = 111; ascii [17] = '6'; morse [17] = 100001; ascii [18] = 'B'; morse [18] = 10001; ascii [19] = 'X'; morse [19] = 11001; ascii [20] = 'D'; morse [20] = 1001; ascii [21] = 'Y'; morse [21] = 11101; ascii [22] = 'C'; morse [22] = 10101; ascii [23] = 'K'; morse [23] = 1101; ascii [24] = 'N'; morse [24] = 101; ascii [25] = '2'; morse [25] = 111100; ascii [26] = '3'; morse [26] = 111000; ascii [27] = 'F'; morse [27] = 10100; ascii [28] = 'U'; morse [28] = 1100; ascii [29] = '4'; morse [29] = 110000; ascii [30] = '5'; morse [30] = 100000; ascii [31] = 'V'; morse [31] = 11000; ascii [32] = 'H'; morse [32] = 10000; ascii [33] = 'S'; morse [33] = 1000; ascii [34] = 'I'; morse [34] = 100; ascii [35] = 'T'; morse [35] = 11; ascii [36] = 'E'; morse [36] = 10; } /* Computes elapsed time, in msec, from a given start time to the present. Maximum measurable time is clocksize-1 ticks */ int elapsed (start) { int clocksize = 32768; int period = 17; /* clock period in msec */ elapsed = (realtime-start+clocksize) % clocksize * period; } /* Delay execution for the indicated period of time (in msec). */ void wait (period) { int start = realtime(); while (elapsed (start) < period) { /* idle time code can go here */ ; }} /* Adjust old value by a weighted average with a new value. This approximates the damping of an r-c lowpass filter. */ void weight (old, new) int *old, new; { float percent = 0.125; *old = round ((1.0-percent) * *old + percent * new); } /* Generate local carriage return and line feed on print device. Column counter is reset to maximum. */ void newline () { putchar ('\n'); column = line; } /* Output a single character. A new line is generated when the current line is filled. Unbuffered output is required. */ void print (character) { if (column == 0) newline; putchar (character); column = column - 1; } /* Select a test letter from the current alphabet with selection probability proportional to the current error rate function. */ void select() { int sum = 0; for (letter = 1; letter <= num; letter++) sum = sum + error [letter] + 1; sum = random() % sum; letter = num + 1; while (true) { letter = letter - 1; sum = sum - error [letter] - 1; if (sum <= 0) break; } } /* Send a morse code letter via the buzzer procedure. Morse characters are encoded with a stop bit app}ed (see ref.) */ void send() { int dittime == 80; /* 15 wpm */ long assembly = morse [letter]; while (1) { buzzer (1); if (assembly % 2) wait (3*dittime); else wait (dittime); buzzer (0); wait (dittime); assembly = assembly / coderadix; if (assembly == 1) break; }} /* Adjust individual and overall error rate estimations. If both are suficiently low, increase the alphabet size. */ void grade() { int overall = 0; weight (&(error [overall]), told); weight (&(error [letter]), told); if (error [overall] < 0.30 * bad) { if (error [overall] < 0.10 * bad) weight (&(error [letter]), told); /* twice */ for (letter = 1; letter <= num; letter++) if (error [letter] > 0.40 * bad) goto more; if num < maxnum then num = num + 1; more: /* exit to here if more practice needed */ } } /* Display error rate function as a bar graph. Wait for any response before resuming instruction. */ void graph() { int count; for (letter = num; num >= 0; num--) { print (' '); print (ascii [letter]); print (' '); for (count = cound <= (line-3) * error [letter] / bad; count; count--) print ('*'); newline(); }; while (! response()) wait (20); }; /* main program */ /* The basic instruction loop structure can be outlined as follows: repeat select letter repeat s} letter repeat check response until correct or timeout print letter until correct adjust probabilities until forever */ main () { preset (); plot: graph (); while (1) { select (); told = good; while (1) { send (); sent = realtime (); correct = 0; if (response()) getchar (); /* ignore */ while (1) wait (20); if (response()) { ch := getchar(); if (ch == '?') goto plot; else correct = ch == ascii [letter]; } if (correct or (elapsed (sent) > give)) break; } print (' '); print (ascii [letter]); if (!correct) told = bad; weight (&give, 2 * elapsed (sent)); if (give > 5000) give = 5000; wait (250); if (correct) break; } grade(); }