From: Eric S. Raymond Date: Mon, 24 Jan 2005 14:32:01 +0000 (+0000) Subject: Initial import X-Git-Tag: 2.0~24 X-Git-Url: http://thyrsus.com/gitweb/?a=commitdiff_plain;h=e58ef91693dac5b3fc6228b37446873747f3126b;p=morse-classic.git Initial import --- e58ef91693dac5b3fc6228b37446873747f3126b diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..10f8603 --- /dev/null +++ b/Makefile @@ -0,0 +1,142 @@ +# Select your audio output device. Current choices are: +# +# Sun: Sun workstation with audio hardware. +# HP: HP9000s700 with audio hardware. +# X11: The X11 window system. +# Indigo: SGI Indigo workstation. +# Linux: IBM PC Console running Linux. +# AdLib: AdLib/SoundBlaster FM chip. +# OSS: Open Sound System /dev/dsp device. +# +# Adding a new device is as simple as creating a new implementation of the +# beep.h interface. See beep*.c for examples. Please send any additions +# to the authors! +# +#DEVICE = Sun +#DEVICE = HP +#DEVICE = X11 +#DEVICE = Indigo +DEVICE = Linux +#DEVICE = AdLib +#DEVICE = OSS + +# +# For termio a la System V, add -DUSG to DEFINES. (Linux in particular needs +# this.) +# +# For alternate random number generator, add either -DUSELRAND for lrand48(), +# or -DUSERAND for (ugh) rand(). Default is -DUSERANDOM for random(). +# +# Define SIGRET to be your signal handler return type if not "void". +# +# Define BSDSIGS or POSIXSIGS, depending on what kind of signals you +# have. "Old-style" signals are not currently supported. Default is BSDSIGS. +# +# If the same speaker making the morse code beeps will also make ^G beeps +# (which occur when you get a letter wrong), you may want to #define +# ERROR_VOLUME to be 1.; this will cause the ^G beeps to be made using +# the same routines that do the morse-code beeps, possibly preventing +# conflicts. (If you prefer quieter error beeps, you can use anything +# between 0. and 1.; if you want it the same volume as the morse-code +# beeps, use "-DERROR_VOLUME=volume". If you don't like the pitch of +# the error tone, you can override the default by #defining ERROR_FREQ to +# be your favorite wake-up alarm frequency, in hertz.) +# +# On a Sun workstation you may want to compile with -DFULLVOLUME (see beepSun.c +# for more details). +# +# If you want the morse to generate all punctuation marks, compile +# with -DALLPUNCTUATION (see README and morse.c for details). +# If you want the morse to generate all procedure signs, compile +# with -DALLPROSIGNS (see README and morse.c for details). +# If you want the morse to generate all international extensions, +# compile with -DALLINTERNATIONAL (see README and morse.c for details). +# +#DEFINES = -DUSG -DUSELRAND -DPOSIXSIGS +DEFINES = -DUSG +#DEFINES = -DUSG -DERROR_VOLUME=1. -DERROR_FREQ=2000. +#DEFINES = -DFULLVOLUME +#DEFINES = + +# The flags necessary to link with the X11 libraries. +#X11LIBS = -L/usr/X11/lib -lX11 -lsocket -lnsl +X11LIBS = -L/usr/X11/lib -lX11 + +# Your favorite C compiler +CC = gcc +#CC = cc + +# Any additional flags your favorite C compiler requires to work. +#CFLAGS = -O -I/usr/X11/include -DSVR4 -DSYSV386 ${DEFINES} +CFLAGS = -O3 -m486 ${DEFINES} + +default: + make test + +morse: morse${DEVICE} + rm -f $@ + ln morse${DEVICE} $@ + +morseSun: morse.o beepSun.o + $(CC) -o $@ morse.o beepSun.o -L/usr/demo/SOUND -laudio -lm + +morseHP: morse.o beepHP.o alarm.o + $(CC) -o $@ morse.o beepHP.o alarm.o -lm + +morseX11: morse.o beepX11.o alarm.o + $(CC) -o $@ morse.o beepX11.o alarm.o $(X11LIBS) + +morseIndigo: morse.o beepIndigo.o + $(CC) -o $@ morse.o beepIndigo.o -laudio -lm + +morseLinux: morse.o beepLinux.o alarm.o + $(CC) -o $@ morse.o beepLinux.o alarm.o -lm + +morseAdLib: morse.o beepAdLib.o alarm.o + $(CC) $(CFLAGS) -o $@ morse.o beepAdLib.o alarm.o -lm + strip $@ + chown root:root $@ + chmod a+sx $@ + +morseOSS: morse.o beepOSS.o + $(CC) $(CFLAGS) -o $@ morse.o beepOSS.o -lm + +QSO: + cd qso; make + ln qso/QSO ./QSO + +# +# "Jocks find quartz glyph, vex BMW." is my attempt to win Stephen J. Gould's +# prize (a copy of all his books) for the first person who can come up with a +# "perfect pangram": a meaningful sentence consisting entirely of common +# English words, with no abbreviations or proper names, that contains each +# letter exactly once. He rejected it because it contains "BMW", alas, but +# he did say it's the closest he's seen so far. - Joe Dellinger +# +test: morse QSO + \rm -f junk + cat test_input > junk + qso/QSO >> junk + # ./morse -v .04 -w 24 -l -e < junk + \rm -f junk + +testqso: morse QSO + qso/QSO | ./morse -v .04 -w 20 -l -e + +all: morse QSO + +clean: + rm -f *.o morse QSO morseSun morseHP morseX11 morseIndigo morseLinux morseAdLib morseOSS + rm -f junk + cd qso; make clean + + +morse.o: beep.h Makefile +beepSun.o: beep.h +beepHP.o: beep.h alarm.h +beepX11.o: beep.h alarm.h +beepIndigo.o: beep.h +beepLinux.o: beep.h alarm.h +beepAdLib.o: beep.h alarm.h +beepOSS.o: beep.h +alarm.o: alarm.h diff --git a/README b/README new file mode 100644 index 0000000..fc3f1e2 --- /dev/null +++ b/README @@ -0,0 +1,100 @@ +A generic morse-code practice utility for Unix systems. + +Currently supported devices: + Sun: Sun workstation with audio hardware. + HP: HP9000s700 with audio hardware. + X11: The X11 window system. (Warning: not all X11 implementations handle + duration and frequency of beeps properly!) + Indigo: The SGI Indigo workstation. (See comments in beepIndigo.c about + caveats and limitations.) + Linux: The IBM PC console speaker. + AdLib: AdLib/SoundBlaster FM chip. (See comments in beepAdLib.c about + caveats and limitations.) + OSS: Open Sound System /dev/dsp device. + +Adding a new device is as simple as creating a new implementation of the +beep.h interface. See beep*.[ch] for examples and documentation. +Please send any additions to the authors!! + +A modified version of the program "QSO" by Paul J. Drongowski is also included. + + Morse Code Table + + A .- N -. 1 .---- . .-.-.- + B -... O --- 2 ..--- , --..-- + C -.-. P .--. 3 ...-- ? ..--.. + D -.. Q --.- 4 ....- ( -.--. + E . R .-. 5 ..... - -....- + F ..-. S ... 6 -.... + G --. T - 7 --... + H .... U ..- 8 ---.. + I .. V ...- 9 ----. + J .--- W .-- 0 ----- + K -.- X -..- / -..-. + L .-.. Y -.-- + .-.-. + M -- Z --.. = -...- + +The following characters are included if one defines ALLPUNCTUATION : + + ) -.--.- " .-..-. _ ..--.- ' .----. + : ---... ; -.-.-. $ ...-..- ! -.-.-- + + Procedural Signals (Prosigns) for Morse Code + + "+" over, end of message + "*" please stand by ( 5 Wait 5 Minutes) (".-...") + "=" (double dash) pause, break for text +CL going off the air (clear) +CQ calling any station +K go, invite any station to transmit + "(" go only, invite a specific station to transmit +R all received OK + "%" end of contact (sent before call) ("...-.-", known also as ) + +The following Procedural Signals (Prosigns) are included if one +defines ALLPROSIGNS : + + "^" new line (".-.-", the same as :a, ae) + "#" invite receiving station to transmit ("-...-.-") + "&" attention ("-.-.-") + "@" understood ("...-.") + +The following characters are included if one defines ALLINTERNATIONAL : + + :a .-.- (also for ae, the same as ) + `a .--.- (also oa, danish a with ring over it) + ch ---- (bar-ch ?, bar-h ?, ISO 8859-1 code 199 and 231 ?) + -d ..--. (eth, overstrike d with -, ISO 8859-1 code 208 and 240) + `e ..-.. + ~n --.-- + :o ---. (also for oe) + :u ..-- (also for ue) + ]p .--.. (thorn, overstrike ] with p, ISO 8859-1 code 222 and 254) + paragraph .-.-.. (ISO 8859-1 code 167 ?) + +Special Characters and Groups (never included, placed here for completeness) : + +Multiplication sign -..- (the same as x, ISO 8859-1 code 215 ?) + +"Mistake, delete the last word" ........ (actually six or more dots are +used for this purpose, eight dots are recommended). + +Cut Numbers : +1 .- 6 -... 1 .- 6 -.... +2 ..- 7 --. 2 ..- 7 -... +3 .-- 8 -.. 3 ...- 8 -.. +4 ...- 9 -. 4 ....- 9 -. +5 ... 0 - 5 . 0 - + +Written by: + Joe Dellinger, Hawaii Institute of Geophysics, University of Hawaii at Manoa + submitted to comp.sources.misc Dec 15 1993 + Revised version submitted to comp.sources.misc Nov 22 1994 + +- joe@sep.stanford.edu + jdellinger@trc.amoco.com + joe@soest.hawaii.edu + +This version dated: + Wed Jan 17 09:43:16 CET 2001 + diff --git a/alarm.c b/alarm.c new file mode 100644 index 0000000..16a9ad2 --- /dev/null +++ b/alarm.c @@ -0,0 +1,105 @@ +/* alarm.c -- seligman 6/92 */ + +/* +-- Implementation of alarm.h +*/ + +#include +#include +#include + +static int alarmPending = 0; /* Nonzero when the alarm is set. */ + +static void ualarm(); +static void AlarmHandler(); + +/* Default BSDSIGS for backwards compatibility of makefile */ +#ifndef BSDSIGS +#ifndef POSIXSIGS +#ifndef OTHERSIGS +#define BSDSIGS +#endif +#endif +#endif + +void AlarmSet(time) + int time; +{ + alarmPending = 1; +#ifdef BSDSIGS + signal(SIGALRM, AlarmHandler); +#else +#ifdef POSIXSIGS + sigset(SIGALRM, AlarmHandler); +#else + /* You're on your own... */ +# endif /*POSIXSIGS*/ +#endif /*BSDSIGS*/ + ualarm(1000 * time, 0); +} + + +/* +-- If an alarm signal is lurking (due to a prior call to SetAlarm), then +-- pause until it arrives. This procedure could have simply been written: +-- if (alarmPending) pause(); +-- but that allows a potential race condition. +*/ +void AlarmWait() +{ +#ifdef BSDSIGS + long savemask = sigblock(sigmask(SIGALRM)); + if (alarmPending) + sigpause(savemask); + sigsetmask(savemask); +#else +#ifdef POSIXSIGS + sighold(SIGALRM); + if (alarmPending) + sigpause(SIGALRM); + sigrelse(SIGALRM); +#else + /* You're on your own */ +#endif /*POSIXSIGS*/ +#endif /*BSDSIGS*/ + +} + + +static void ualarm(us) + unsigned us; +{ + struct itimerval rttimer, old_rttimer; + + rttimer.it_value.tv_sec = us / 1000000; + rttimer.it_value.tv_usec = us % 1000000; + rttimer.it_interval.tv_sec = 0; + rttimer.it_interval.tv_usec = 0; + if (setitimer(ITIMER_REAL, &rttimer, &old_rttimer)) { + perror("ualarm"); + exit(1); + } +} + + +#ifdef __hpux + +static void AlarmHandler(sig, code, scp) + int sig; + int code; + struct sigcontext *scp; +{ + alarmPending = 0; + /* Prevent alarm signal from interrupting any pending read. */ + if (scp->sc_syscall == SYS_READ) + scp->sc_syscall_action = SIG_RESTART; +} + +#else + +static void AlarmHandler() +{ + alarmPending = 0; +} + +#endif __hpux diff --git a/alarm.h b/alarm.h new file mode 100644 index 0000000..13aec54 --- /dev/null +++ b/alarm.h @@ -0,0 +1,20 @@ +/* alarm.h -- seligman 6/92 */ + +/* +-- Routines for using the system interval timer to time beeps. Useful +-- for implementing the functions in beep.h on systems that don't provide +-- a more straightforward BeepWait() equivalent. +-- +-- These routines use the ALRM signal. +*/ + + +/* +-- Set the alarm for a time specified in ms. +*/ +void AlarmSet(/*int time*/); + +/* +-- Wait for the alarm, or return immediately if the alarm isn't set. +*/ +void AlarmWait(); diff --git a/beep.h b/beep.h new file mode 100644 index 0000000..2de914e --- /dev/null +++ b/beep.h @@ -0,0 +1,59 @@ +/* beep.h -- seligman 5/92 */ + +/* +-- Machine-dependent code for sounding a beep. +*/ + +#ifndef _BEEP_H +#define _BEEP_H + + +/* +-- Called exactly once, before any other function in this interface. +-- Returns nonzero on error. +*/ +int BeepInit(); + + +/* +-- Sound a beep for a time specified in ms. +-- The volume is in the range [0..100], and the pitch is in Hz. +-- +-- May return immediately, after the sounding of the beep is completed, +-- or any time in between. May be called while a previous beep is still +-- sounding, in which case the previous beep finishes before the new one +-- begins. Overall timing will be much better if this routine can return +-- during the sounding of the beep, especially if it's a "zero-volume beep", +-- meaning it's really just the timed pause of silence between tones. +-- ("morse.c" tries to do all its thinking during the pauses between beeps, +-- mostly in the longer ones between words.) +-- +-- May use the ALRM signal for timing. +-- +-- Returns nonzero on error. +*/ +int Beep(/*int time, int volume, int pitch*/); + + +/* +-- Wait until any currently sounding beeps have completed. +-- Returns nonzero on error. +*/ +int BeepWait(); + + +/* +-- Clean up any altered state before exiting or suspending the program. +-- Returns nonzero on error. +*/ +int BeepCleanup(); + + +/* +-- Restore the world when program is resumed after having been suspended. +-- Returns nonzero on error. +*/ +int BeepResume(); + + +#endif _BEEP_H diff --git a/beepAdLib.c b/beepAdLib.c new file mode 100644 index 0000000..57f4b17 --- /dev/null +++ b/beepAdLib.c @@ -0,0 +1,168 @@ +/* beepAdLib.c -- Jacek M. Holeczek 3/2000 */ +/* A lot of code taken from the sccw Linux Morse program by */ +/* Steven J. Merrifield VK3ESM sjm@ee.latrobe.edu.au */ + +/* + Implementation of beep for the AdLib/SoundBlaster FM chip. + + Warning: All low-level access is achieved by writing directly to the + IO ports of the soundcard, hence this program must be compiled using + at least -O2 and run setuid root. Hence, this program must be installed + by root, but can then be run by a normal user. + + Must compile with -O2 -lm and optionally -DDEBUG_FREQ +*/ + +#include +#include +#include +#include +#include + +#include "alarm.h" +#include "beep.h" + +/* Define it if you want to reset the AdLib/SoundBlaster FM chip. */ +#undef RESET_ADLIB + +/* The port addresses for the FM chip is always the same. */ +#define reg_port 0x0388 +#define data_port reg_port+1 + +void wr_register(int index, int data) /* See the PC Game Programmers Encyclopedia */ +{ + int i,temp; + outb(index,reg_port); + for (i=1;i<7;i++) + temp = inb(reg_port); + outb(data,data_port); + for (i=1;i<36;i++) + temp = inb(reg_port); +} + +int AdLib_found(void) /* See the PC Game Programmers Encyclopedia */ +{ + long temp1,temp2; + wr_register(4,0x60); + wr_register(4,0x80); + temp1 = inb(reg_port); + wr_register(2,0xFF); + wr_register(4,0x21); + sleep(1); + temp2 = inb(reg_port); + wr_register(4,0x60); + wr_register(4,0x80); + if (((temp1 & 0xE0) == 0) && ((temp2 & 0xE0) == 0xC0)) + { + return 0; /* card found and no errors */ + } else { + return 1; /* card not found or errors */ + } +} + +#ifdef RESET_ADLIB +void Reset_AdLib(void) /* Set all registers to 0 */ +{ + int i; + for (i=1;i<245;i++) + wr_register(i,0); +} +#endif + +int BeepInit(void) +{ + if (geteuid()) + { + fprintf(stderr,"This program must run as root (setuid root).\n"); + return 1; + } + ioperm(reg_port,17,1); + ioperm(data_port,17,1); + if (AdLib_found()) + { + fprintf(stderr,"The AdLib/SoundBlaster card not found.\n"); + ioperm(reg_port,17,0); + ioperm(data_port,17,0); + return 2; + } +#ifdef RESET_ADLIB + Reset_AdLib(); +#else + wr_register(0xB0,0x00); /* Turn off sound, was 0x11 */ +#endif + return 0; +} + +int Beep(int time, int volume, int pitch) +{ + int blk = -1; + int f_num; + int A0_reg,B0_reg,B0_reg10,B0_reg432; + + BeepWait(); + AlarmSet(time); + + if (volume > 0 && pitch > 0) + { + wr_register(0x20,1); /* Play sound at fundamental freq. */ + wr_register(0x23,1); /* Carrier at specified freq. */ + wr_register(0x43,(64*(100-volume)/100)); /* Volume 0-64 */ + + wr_register(0x63,0xF0); /* Carrier attack = fast, decay = slow */ + wr_register(0x83,0xFF); /* Carrier sustain = soft, release = fast */ + + /* Would you belive it took three days to work out how to do this ? */ + do + { + blk++; + f_num = (int)(((float)pitch / 50000.0) / (1/(pow(2,abs(blk-20))))); + } while (f_num > 1023); + A0_reg = f_num & 0x00FF; + B0_reg10 = (f_num & 0x0300) >> 8; + B0_reg432 = (blk << 2) & 0x00FF; /* Do I really need to & 0x00ff ?? */ + B0_reg = (32 | B0_reg432 | B0_reg10) & 0x00FF; + + wr_register(0xA0,A0_reg); + wr_register(0xB0,B0_reg); + +#ifdef DEBUG_FREQ + printf("blk = %d f_num = %d\n",blk,f_num); + printf("B0_reg432 = %2x\n",B0_reg432); /* bits 4 3 2 */ + printf("B0_reg10 = %2x\n",B0_reg10); /* bits 1 0 */ + printf("B0_reg = %2x A0_reg = %2x\n\n\n",B0_reg,A0_reg); +#endif + } + + return 0; +} + +int BeepWait(void) +{ + AlarmWait(); + wr_register(0xB0,0x00); /* Turn off sound, was 0x11 */ + return 0; +} + +int BeepCleanup(void) +{ +#ifdef RESET_ADLIB + Reset_AdLib(); +#else + wr_register(0xB0,0x00); /* Turn off sound, was 0x11 */ +#endif + ioperm(reg_port,17,0); + ioperm(data_port,17,0); + return 0; +} + +int BeepResume(void) +{ + ioperm(reg_port,17,1); + ioperm(data_port,17,1); +#ifdef RESET_ADLIB + Reset_AdLib(); +#else + wr_register(0xB0,0x00); /* Turn off sound, was 0x11 */ +#endif + return 0; +} diff --git a/beepHP.c b/beepHP.c new file mode 100644 index 0000000..4e9607c --- /dev/null +++ b/beepHP.c @@ -0,0 +1,177 @@ +/* beepHP.c -- seligman 5/92 */ + +/* +-- Implementation of beep.h for HP9000s700 with audio hardware. +-- +-- If the environment variable SPEAKER begins with "E", the external +-- audio connection is used. If it begins with "I", the internal speaker +-- is used. Otherwise both are used. +-- +-- Compile with the math library "-lm". +*/ + +#include "beep.h" +#include "alarm.h" +#include +#include +#include +#include +#include +#include +#include + + +static char *AudioDev; + +static int DoBeep(); + + +int BeepInit() +{ + char *speaker = getenv("SPEAKER"); + int fd; + + AudioDev = speaker && toupper(speaker[0])=='E' ? "/dev/audioEL" + : speaker && toupper(speaker[0])=='I' ? "/dev/audioIL" + : "/dev/audioBL"; + + /* + -- A quick check to ensure that we can open the device. + */ + if ((fd = open(AudioDev, O_WRONLY)) < 0) { + perror("?? Error opening audio device"); + return 1; + } + close(fd); + return 0; +} + + +/* +-- Optimized for the case where volume & pitch don't change (except possibly +-- to zero and back) between successive calls. +*/ +int Beep(time, volume, pitch) + int time, volume, pitch; +{ + int rc; + + AlarmWait(); + if (volume != 0 && pitch != 0) + if ((rc = DoBeep(time, volume, pitch)) != 0) + return rc; + AlarmSet(time); + return 0; +} + + +int BeepWait() +{ + AlarmWait(); + return 0; +} + + +int BeepCleanup() +{ + return 0; +} + + +int BeepResume() +{ + return 0; +} + + +/*----------------------------------------*/ + +#define SamplingRate 8000 /* audio device samples per second */ +#define NumRamp (SamplingRate / 200) /* # samples in 5ms ramp up/down */ + +typedef short int Sample; +#define MaxSample (Sample)0x7FFF + + +/* +-- Sound a beep. +-- Optimized for the case where volume & pitch don't change between successive +-- calls. +*/ +static int DoBeep(time, volume, pitch) + int time, volume, pitch; +{ + static int audioDevFD = -1; /* -1 indicates not yet open. */ + + /* Save sample buffer from call to call, and reuse it when possible. */ + static Sample *buf = NULL; + static int bufLen = 0; + static int bufVolume = 0; + static int bufPitch = 0; + + Sample rampbuf[NumRamp]; /* Sample buffer for ramp-down. */ + + /* Are volume and pitch same as previous call? */ + int sameVP = (volume == bufVolume && pitch == bufPitch); + + int i, j; + int numSamples = SamplingRate * time / 1000; + + /* + -- It seems that the first write to the audio device after it's + -- been opened must be the longest. + */ + if (numSamples > bufLen) { + if (audioDevFD >= 0) close(audioDevFD); + audioDevFD = -1; + } + if (audioDevFD < 0) { + if ((audioDevFD = open(AudioDev, O_WRONLY)) < 0) { + perror("?? Error opening audio device"); + return 1; + } + } + + /* + -- Allocate space for sample buffer. Set "i" to lowest index in + -- need of updating. + */ + i = sameVP ? bufLen : 0; + if (numSamples > bufLen) { + if (sameVP) { + buf = realloc(buf, numSamples * sizeof(Sample)); + } else { + if (buf != NULL) free(buf); + bufVolume = volume; + bufPitch = pitch; + buf = malloc(numSamples * sizeof(Sample)); + } + if (buf == NULL) { + fprintf(stderr, "?? Buy more memory.\n"); + return 1; + } + bufLen = numSamples; + } + + /* + -- Update any newly-allocated tail of sample buffer. The ramp-up is + -- handled here (since it always occurs at the same place); the ramp-down + -- is handled later. + */ + while (i < bufLen) { + double rampScale = (i < NumRamp) ? (double) i / NumRamp : 1; + double t = (double) i / SamplingRate; + buf[i++] = + sin(2 * M_PI * pitch * t) * MaxSample * rampScale * volume / 100; + } + + /* Sound the beep, except for the ramp-down. */ + write(audioDevFD, buf, (numSamples - NumRamp) * sizeof(Sample)); + + /* Ramp-down */ + for (i = 0, j = numSamples-NumRamp; i < NumRamp; i++, j++) + rampbuf[i] = buf[j] * ((double) (NumRamp - i) / NumRamp); + write(audioDevFD, rampbuf, sizeof rampbuf); + + return 0; +} diff --git a/beepIndigo.c b/beepIndigo.c new file mode 100644 index 0000000..59869ee --- /dev/null +++ b/beepIndigo.c @@ -0,0 +1,248 @@ +/* beepIndigo.c John R. Murray murray@scri.fsu.edu Jan. 4, 1994. Implementation + * of beep.h for SGI Indigos. Basically a quick rewrite of beepSun.c, written + * by seligman et al 6/92 + */ + +/* + * If the audio library or audio include files are missing, you need + * to get sound support off the SGI distribution tapes or CD. + * + * Defining FULLVOLUME has no effect. See notes below. + */ + +/* NOTES: This will not compile on the older Personal Irises (4D/20 and 4D/25). +-- It should work for newer PIs (4D/30 and 4D/35) on SGI models newer than the +-- Indigo (Indy and Indigo^2), but has not been tested. Basically, anything +-- that uses the Audio Library (ALwhatever function names), and *not* the +-- /dev/audio interface, should work. +-- +-- The sound library on my machine is only a partial implementation, so some +-- functionality in beep.h is missing. Notably: +-- +-- 1) No software volume control. (relative volume with -v still works, tho) +-- +-- 2) No software control of sampling rate. +-- +-- These parameters are controllable through the Audio Control Panel, +-- accessible through the Tools submenu, or by running /usr/sbin/apanel. +-- NOTE: This code assumes you have the output sampling rate set to 48 KHz +-- (the default). If the tone is too low and the code timing is too slow, +-- your output sampling rate is incorrect and will need to be set to 48 KHz. +-- +-- If you implement volume and sampling rate control, please be sure to send +-- the updated code to the writer of morse.c, joe@montebello.soest.hawaii.edu, +-- or to me and I'll forward it. +*/ + +#include "beep.h" +#include +#include + +#define ABUFSIZ (100000) /* 100000 is default buffer size */ +#define RATE (48000) /*48 K samples = 1 second (assuming it's set to the default)*/ +#define RAMP (RATE * 10 / 1000) /* 10 millisecond ramp */ +#define MAXTIME (ABUFSIZ) /* If STEREO, MAXTIME = ABUFSIZ / 2 */ +#define ASCALE (100) /* multiplier to size waveform to port width (assuming 8bit) */ + +ALport outPort; + +void writeSound(port, buffer, len) +ALport port; +signed char buffer[]; +unsigned long len; +{ + signed char *bufp; + unsigned long len2, fill; + + bufp = buffer; + len2 = len; + while ((fill = ALgetfillable(port)) < len2) + { + if (fill) + { + /* write as much as we can */ + ALwritesamps(port, bufp, fill); + bufp += fill; + len2 -= fill; + } + else + { + /* wait 1/CLK_TCK seconds (1/100 sec on my machine) */ + sginap(1); + } + } + ALwritesamps(port, bufp, len2); +} + +int +BeepInit () +{ + + ALconfig config; + + config = ALnewconfig(); + /* starting with default config (stereo, 16 bit wide samples, 100k buffer) */ + /* NOTE: we're assuming the sampling rate is set to the default (48K) */ + /* (software control of sampling rate is not fully implemented by SGI yet */ + + if (config == 0) + { + /* Uh oh! */ + return 1; + } + else + { + /* this is just to avoid rewriting much of Beep, we could + * run 24 bit stereo if we wanted. + */ + ALsetwidth(config, AL_SAMPLE_8); + ALsetchannels(config, AL_MONO); /* if STEREO, fix the #define of MAXTIME */ + ALsetqueuesize(config, ABUFSIZ); + +#ifdef FULLVOLUME + /* software volume control not fully implemented by SGI yet */ +#endif + + if (0 == (outPort = ALopenport("morse", "w", config))) + /* another problem */ + return 1; + ALfreeconfig(config); + return 0; + } +} + + +int +Beep (time, volume, pitch) + int time, volume, pitch; +{ +static signed char silence[MAXTIME]; +static signed char soundbuf[MAXTIME - RAMP]; +static signed char ramp_down[RAMP]; +static long last_n = 0xFFFFFFFFL; +static int last_pitch = -1, last_volume = -1; +static int first_time = 1; + +int i, cycle; +unsigned long n, first_len, down_len; +double dt; + +/* + * Initialize the sound of silence + */ + if (first_time == 1) + { + for (i = 0; i < MAXTIME; i++) + silence[i] = 0; + first_time = 0; + } + + +/* + * Finagle the number of samples + */ + n = (time / 1000.) * RATE; /* Number samples in tone time */ + n = n < MAXTIME ? n : MAXTIME; /* clip to buffer size */ + n = n < 2 * RAMP ? 2 * RAMP : n; /* leave room for ramps */ + +/* + * Catch stupidity + */ + if (pitch <= 0) + volume = 0; + + + if (volume <= 0) + { + writeSound(outPort, silence, n); + } + else + { +/* + * clip to Nyquist + * (Of course this means that if you ask for too high a frequency you + * just get silence, since you sample all the zero-crossings of the + * sine wave.) + */ + pitch = pitch < RATE / 2 ? pitch : RATE / 2; + cycle = ((RATE + 1.e-6) / pitch); /* samples per cycle */ + + if (cycle > MAXTIME / 2) + cycle = MAXTIME / 2; + + /* round down length of (rampup + mesa top) to integral cycle */ + first_len = ((n - RAMP) / cycle) * cycle; + if (first_len < cycle) + first_len = cycle; + if (first_len > MAXTIME - RAMP) + first_len = MAXTIME - RAMP; + + /* round down length of (rampdown) to integral cycle */ + down_len = ((RAMP) / cycle) * cycle; + if (down_len < cycle) + down_len = cycle; + if (down_len > RAMP) + down_len = RAMP; + +/* + * Can we just reuse what we had before? + */ + if (pitch != last_pitch || n > last_n || volume != last_volume) + { + last_pitch = pitch; + last_n = n; + last_volume = volume; + + dt = 2. * 3.14159 / cycle; /* sine scale factor */ + +/* Ramp up; begin with silence */ + + for (i = 0; i < RAMP; i++) + { + soundbuf[i] = ASCALE * ((float) i / RAMP) * (volume / 100.) + * sin (i * dt); + } + +/* Mesa top */ + for (i = RAMP; i < first_len; i++) + { + soundbuf[i] = ASCALE * 1. * (volume / 100.) * sin (i * dt); + } + +/* Ramp down; end with silence */ + for (i = 0; i < down_len; i++) + { + ramp_down[i] = ASCALE * (1. - (float) (i + 1) / down_len) + * (volume / 100.) * sin (i * dt); + } + } + writeSound(outPort, soundbuf, first_len); + writeSound(outPort, ramp_down, down_len); + } + + return 0; +} + + +int +BeepWait () +{ + while (ALgetfilled(outPort) > 0) + sginap(1); /* wait 1/CLK_TCK seconds (1/100th second on my machine) */ + return 0; +} + + +int +BeepCleanup () +{ + ALcloseport(outPort); + return 0; +} + + +int +BeepResume () +{ + return BeepInit(); +} diff --git a/beepLinux.c b/beepLinux.c new file mode 100644 index 0000000..add29bb --- /dev/null +++ b/beepLinux.c @@ -0,0 +1,82 @@ +/* + * From: jmorriso@bogomips.ee.ubc.ca (John Paul Morrison) + * Subject: beepLinux.c + * To: joe@montebello.soest.hawaii.edu + * Date: Fri, 18 Nov 1994 15:32:33 -0800 (PST) + * X-Linux: watch for it: Linux 94 aka "Helsinki" + * + * beepLinux.c for morse (lightly tested, seems to work fine) + */ + +/* + * beep for the Linux console (PC Speaker) + * + * (beepSun.c might work if you have a sound card installed and /dev/audio) + * + * by John Paul Morrison + * + */ + +/* beepLinux.c -- 11/94 */ + +#include +#include +#include +#include +#include +#include +#include "alarm.h" +#include "beep.h" + + +static int fd; + +int BeepInit() +{ + if ((fd = open("/dev/console",O_WRONLY)) == -1) + { + fprintf(stderr,"You have no permissions to use /dev/console (chmod a+w).\n"); + return 1; + } + return 0; +} + +int Beep(time, volume, pitch) +int time, volume, pitch; +{ + int count; + + AlarmWait(); + if (volume == 0) + count = 0; + else + count = (1193180 / pitch) & 0xffff; + + ioctl(fd, KDMKTONE, (time << 16) | count); + AlarmSet(time); + return 0; +} + +int BeepWait() +{ + AlarmWait(); + return 0; +} + +int BeepCleanup() +{ + close(fd); + return 0; +} + +int BeepResume() +{ + return 0; +} + +/* + * --------------------------------------------------------------------------- + * BogoMIPS Research Labs -- bogosity research & simulation -- VE7JPM -- + * jmorriso@bogomips.ee.ubc.ca ve7jpm@ve7jpm.ampr.org jmorriso@ve7ubc.ampr.org + * --------------------------------------------------------------------------- + */ diff --git a/beepOSS.c b/beepOSS.c new file mode 100644 index 0000000..6573539 --- /dev/null +++ b/beepOSS.c @@ -0,0 +1,207 @@ +/* beepOSS.c -- Jacek M. Holeczek 3/2000 */ +/* A lot of code taken from the beepSun.c */ + +/* + Implementation of beep for the Open Sound System /dev/dsp device. +*/ + +#include +#include +#include +#include +#include +#include + +#include "beep.h" + +#define DEVICE_NAME "/dev/dsp" +#define RATE (8000) /* 8 K samples = 1 second */ +#define RAMP (RATE * 5 / 1000) /* 5 millisecond ramp */ +#define MAXTIME (2 * RATE) /* 2 seconds max tone time */ + +static int audio = 0; +static int channels = 0; /* 1=mono, 2=stereo */ +static unsigned char silence[2*MAXTIME]; +static unsigned char soundbuf[2*(MAXTIME - RAMP)]; +static unsigned char ramp_down[2*RAMP]; + +int BeepInit (void) +{ + int i; + + /* + * Initialize the sound of silence + */ + for (i = 0; i < 2*MAXTIME; i++) + silence[i] = 128; + + audio = open(DEVICE_NAME, O_WRONLY, 0); + if (audio < 0) + { + return 1; /* not o.k. */ + } else { + + i = 0x00ff0008; /* 255 fragments, 256 bytes each */ + if (ioctl(audio, SNDCTL_DSP_SETFRAGMENT, &i)) + { + fprintf(stderr, "SNDCTL_DSP_SETFRAGMENT error : 0x%08x\n", i); + return 2; + } + + channels = 1; /* 1 channel = mono */ + if (ioctl(audio, SNDCTL_DSP_CHANNELS, &channels)==-1) + { + fprintf(stderr, "SNDCTL_DSP_CHANNELS error : %i\n", channels); + return 3; + } + if (channels != 1) + { + channels = 2; /* 2 channels = stereo */ + if (ioctl(audio, SNDCTL_DSP_CHANNELS, &channels)==-1) + { + fprintf(stderr, "SNDCTL_DSP_CHANNELS error : %i\n", channels); + return 4; + } + } + if (!(channels == 1 || channels == 2)) + { + fprintf(stderr, "Neither mono nor stereo mode could be set.\n"); + return 5; + } + + i = AFMT_U8; /* standard unsigned 8 bit audio encoding */ + if (ioctl(audio, SNDCTL_DSP_SETFMT, &i)==-1) + { + fprintf(stderr, "SNDCTL_DSP_SETFMT error : %i\n", i); + return 6; + } + + i = RATE; /* sampling rate RATE */ + if (ioctl(audio, SNDCTL_DSP_SPEED, &i)==-1) + { + fprintf(stderr, "SNDCTL_DSP_SPEED error : %i\n", i); + return 7; + } + + write (audio, silence, channels*2); + BeepWait(); + return 0; /* o.k. */ + } +} + +int Beep (int time, int volume, int pitch) +{ + static int last_pitch = -1, last_n = -1, last_volume = -1; + int i, n, first_len, down_len, cycle; + double dt; + + /* + * Finagle the number of samples + */ + n = (time / 1000.) * RATE; /* Number samples in tone time */ + n = n < MAXTIME ? n : MAXTIME; /* clip to buffer size */ + n = n < 2 * RAMP ? 2 * RAMP : n; /* leave room for ramps */ + + /* + * Catch stupidity + */ + if (pitch <= 0) + volume = 0; + + if (volume <= 0) + { + write (audio, silence, channels*n); + } + else + { + /* + * clip to Nyquist + * (Of course this means that if you ask for too high a frequency you + * just get silence, since you sample all the zero-crossings of the + * sine wave.) + */ + pitch = pitch < RATE / 2 ? pitch : RATE / 2; + cycle = ((RATE + 1.e-6) / pitch); /* samples per cycle */ + + if (cycle > MAXTIME / 2) + cycle = MAXTIME / 2; + + /* round down length of (rampup + mesa top) to integral cycle */ + first_len = ((n - RAMP) / cycle) * cycle; + if (first_len < cycle) + first_len = cycle; + if (first_len > MAXTIME - RAMP) + first_len = MAXTIME - RAMP; + + /* round down length of (rampdown) to integral cycle */ + down_len = ((RAMP) / cycle) * cycle; + if (down_len < cycle) + down_len = cycle; + if (down_len > RAMP) + down_len = RAMP; + + /* + * Can we just reuse what we had before? + */ + if (pitch != last_pitch || n > last_n || volume != last_volume) + { + last_pitch = pitch; + last_n = n; + last_volume = volume; + + dt = 2. * M_PI / cycle; /* sine scale factor */ + + /* Ramp up; begin with silence */ + + for (i = 0; i < RAMP; i++) + { + soundbuf[i*channels] = rint( 128. + + ((float) i / RAMP) * (volume * 1.26) * sin (i * dt) + ); + if (channels == 2) + soundbuf[i*channels+1] = soundbuf[i*channels]; + } + + /* Mesa top */ + for (i = RAMP; i < first_len; i++) + { + soundbuf[i*channels] = rint( 128. + + 1. * (volume * 1.26) * sin (i * dt) + ); + if (channels == 2) + soundbuf[i*channels+1] = soundbuf[i*channels]; + } + + /* Ramp down; end with silence */ + for (i = 0; i < down_len; i++) + { + ramp_down[i*channels] = rint( 128. + + (1. - (float) (i + 1) / down_len) * (volume * 1.26) * sin (i * dt) + ); + if (channels == 2) + ramp_down[i*channels+1] = ramp_down[i*channels]; + } + } + + write (audio, soundbuf, channels*first_len); + write (audio, ramp_down, channels*down_len); + } + + return 0; +} + +int BeepWait (void) +{ + ioctl (audio, SNDCTL_DSP_SYNC, 0); + return 0; +} + +int BeepCleanup (void) +{ + return 0; +} + +int BeepResume (void) +{ + return 0; +} diff --git a/beepSun.c b/beepSun.c new file mode 100644 index 0000000..a6340eb --- /dev/null +++ b/beepSun.c @@ -0,0 +1,200 @@ +/* + * If the audio library or audio include files are missing, you need + * to get /usr/demo/SOUND off the Sun OS tapes or CD. + * + * If FULLVOLUME is defined this routine will turn your audio device up to + * full volume; it will not attempt to reset the current speaker / jack choice. + */ + +/* beepSun.c -- seligman 6/92 */ + +/* +-- Implementation of beep.h for Sun. +-- Rick to Joe to Scott to Joe. +*/ + +/* Date: Fri, 2 Aug 91 08:26:08 PDT */ +/* From: rick@st.unocal.com (Richard Ottolini) */ + +/* Revised: Joe Dellinger */ +/* Sat Nov 28 02:30:32 HST 1992 */ + +/* Revised: Brian Suggs (fixed the "annoying click" bug) */ +/* Fri Sep 16 12:29:13 PDT 1994 */ + +#include "beep.h" +#include +#include +#include +#include +#include +#include + + +#define RATE (8000) /* 8 K samples = 1 second */ +#define RAMP (RATE * 5 / 1000) /* 5 millisecond ramp */ +#define MAXTIME (2 * RATE) /* 2 seconds max tone time */ + +static int audio; +static audio_info_t audio_stat; + +int +BeepInit () +{ + audio = open ("/dev/audio", O_WRONLY); + + if (audio < 0) + /* Uh oh! */ + return 1; + else + { +#ifdef FULLVOLUME + /* + * Turn the speaker (or headphone, whichever the user has it set to) + * up to full volume. + */ + AUDIO_INITINFO(&audio_stat); + audio_stat.play.gain = AUDIO_MAX_GAIN; + ioctl (audio, AUDIO_SETINFO, &audio_stat); +#endif + + return 0; + } +} + + +int +Beep (time, volume, pitch) + int time, volume, pitch; +{ +static unsigned char silence[MAXTIME]; +static unsigned char soundbuf[MAXTIME - RAMP]; +static unsigned char ramp_down[RAMP]; +static int last_pitch = -1, last_n = -1, last_volume = -1; +static int first_time = 1; + +int i, n, first_len, down_len, cycle; +double dt; + +/* + * Initialize the sound of silence + */ + if (first_time == 1) + { + for (i = 0; i < MAXTIME; i++) + silence[i] = audio_l2u (0); + first_time = 0; + } + + +/* + * Finagle the number of samples + */ + n = (time / 1000.) * RATE; /* Number samples in tone time */ + n = n < MAXTIME ? n : MAXTIME; /* clip to buffer size */ + n = n < 2 * RAMP ? 2 * RAMP : n; /* leave room for ramps */ + +/* + * Catch stupidity + */ + if (pitch <= 0) + volume = 0; + + + if (volume <= 0) + { + write (audio, silence, n); + } + else + { +/* + * clip to Nyquist + * (Of course this means that if you ask for too high a frequency you + * just get silence, since you sample all the zero-crossings of the + * sine wave.) + */ + pitch = pitch < RATE / 2 ? pitch : RATE / 2; + cycle = ((RATE + 1.e-6) / pitch); /* samples per cycle */ + + if (cycle > MAXTIME / 2) + cycle = MAXTIME / 2; + + /* round down length of (rampup + mesa top) to integral cycle */ + first_len = ((n - RAMP) / cycle) * cycle; + if (first_len < cycle) + first_len = cycle; + if (first_len > MAXTIME - RAMP) + first_len = MAXTIME - RAMP; + + /* round down length of (rampdown) to integral cycle */ + down_len = ((RAMP) / cycle) * cycle; + if (down_len < cycle) + down_len = cycle; + if (down_len > RAMP) + down_len = RAMP; + +/* + * Can we just reuse what we had before? + */ + if (pitch != last_pitch || n > last_n || volume != last_volume) + { + last_pitch = pitch; + last_n = n; + last_volume = volume; + + dt = 2. * M_PI / cycle; /* sine scale factor */ + +/* Ramp up; begin with silence */ + + for (i = 0; i < RAMP; i++) + { + soundbuf[i] = audio_l2u (audio_d2l ( + ((float) i / RAMP) * (volume / 100.) * sin (i * dt) + )); + } + +/* Mesa top */ + for (i = RAMP; i < first_len; i++) + { + soundbuf[i] = audio_l2u (audio_d2l ( + 1. * (volume / 100.) * sin (i * dt) + )); + } + +/* Ramp down; end with silence */ + for (i = 0; i < down_len; i++) + { + ramp_down[i] = audio_l2u (audio_d2l ( + (1. - (float) (i + 1) / down_len) * (volume / 100.) * sin (i * dt) + )); + } + } + + write (audio, soundbuf, first_len); + write (audio, ramp_down, down_len); + } + + return 0; +} + + +int +BeepWait () +{ + ioctl (audio, AUDIO_DRAIN); + return 0; +} + + +int +BeepCleanup () +{ + return 0; +} + + +int +BeepResume () +{ + return 0; +} diff --git a/beepX11.c b/beepX11.c new file mode 100644 index 0000000..dc9c444 --- /dev/null +++ b/beepX11.c @@ -0,0 +1,81 @@ +/* beepX11.c -- seligman 5/92 */ + +/* +-- Implementation of beep.h for X11. +-- +-- Compile with the library "-lX11". +*/ + +#include +#include +#include "alarm.h" +#include "beep.h" + +static Display *dpy = 0; +static XKeyboardControl initialState; + +#define BellFlags (KBBellPercent | KBBellPitch | KBBellDuration) + + +int BeepInit() +{ + XKeyboardState state; + + if (! (dpy = XOpenDisplay(0))) { + perror("Couldn't open display"); + return 1; + } + + /* Save initial state so it can be restored later. */ + XGetKeyboardControl(dpy, &state); + initialState.bell_duration = state.bell_duration; + initialState.bell_percent = state.bell_percent; + initialState.bell_pitch = state.bell_pitch; + + return 0; +} + + +int Beep(time, volume, pitch) + int time, volume, pitch; +{ + XKeyboardControl values; + + AlarmWait(); + + if (volume != 0 && pitch != 0) { + values.bell_duration = time; + values.bell_percent = 100; + values.bell_pitch = pitch; + + XChangeKeyboardControl(dpy, BellFlags, &values); + XBell(dpy, volume - 100); + XFlush(dpy); + } + + AlarmSet(time); + return 0; +} + + +int BeepWait() +{ + AlarmWait(); + return 0; +} + + +int BeepCleanup() +{ + if (dpy != 0) { + XChangeKeyboardControl(dpy, BellFlags, &initialState); + XFlush(dpy); + } + return 0; +} + + +int BeepResume() +{ + return 0; +} diff --git a/morse.c b/morse.c new file mode 100644 index 0000000..b5ad376 --- /dev/null +++ b/morse.c @@ -0,0 +1,2073 @@ +/* + * A morse code practice utility. (Contains those characters that can appear + * on the FCC ham license exam.) + * + * Running "morse" without arguments or input gives self-doc. + * + * It doesn't keep PERFECT time, but it seems reasonably close + * for reasonable word speeds on my slow SUN IPC! + * + * Joe Dellinger + * Tue Aug 11 14:01:02 HST 1992 + * University of Hawaii at Manoa + * + * Revised by Joe: + * Thu Nov 26 03:24:19 HST 1992 + * + * Finally got around to getting my license, April 1994: WH6UR + * Moved to Tulsa, Oklahoma: July 1994 + * + * Legal stuff: + * This code is (ridiculously) heavily modified from morse.c from the Reno UNIX + * distribution. I (Joe) also used slightly modified versions of a subroutine + * from Richard Ottolini at Unocal for Sun workstation tone generation. + * Scott Seligman at Stanford added support for other sorts of devices and + * made several other changes. John Shalamskas helped test and made comments. + * + * I don't think anybody cares if you redistribute this, modify it, etc... + * But don't claim you wrote it, try to sell it, or take anyone's name out + * of the code! If you modify it, PLEASE INDICATE WHEN, WHERE, AND HOW + * YOU MODIFIED IT IN THE COMMENT BELOW. Please. + */ +/* + * Joe Dellinger, UH Manoa, joe@montebello.soest.hawaii.edu, June 1992: + * inserted this sample modification log entry. + * + * Marc Unangst, N8VRH, mju@mudos.ann-arbor.mi.us, Dec 1992: + * added System V support. + * + * Joe Dellinger, Nov 1994: + * Fixed a bug in the termio calls for the -DUSG case. + * Added an option to make the "error" beep come out via the beep + * routines instead of by printing a control-G. + * + * Jacek M. Holeczek, March/April 2000: + * Fixed some source code files so that no warnings from -Wall appear. + * The character set made compatible with the International Morse Code. + * Added the -n NUM, -R NUM, -N NUM, -C 'STRING', -q options. + * Added beep for the AdLib/SoundBlaster FM chip and for the Open Sound + * System (OSS) /dev/dsp device. + * Note: The code uses directly ANSI sequences "\E[7m" to turn the inverse + * video mode on and "\E[0m" to turn all video attributes off. + * One should rewrite the openterminal function to provide the + * enter_standout_mode "smso/so" and exit_standout_mode "rmso/se" + * terminfo/termcap terminal string capabilities. + */ + +/* + * The following stuff is here because this started life as a routine from + * the Reno UNIX distribution. (It's also a good boilerplate bit of legalese + * to have in there anyway.) + */ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1988 Regents of the University of California.\n\ + All rights reserved.\n"; +char copyleftjoe[] = +"@(#) Copyright (c) 1992 Joe Dellinger, University of Hawaii at Manoa.\n"; +#endif /* not lint */ + +/* + *============================================================================ + * Here starts the code! + *============================================================================ + */ +/* + * Useful for seeing what the interleaved reading and writing loops are + * really up to. + * + * #define DEBUG + * + * If you want to be overwhelmed with information about the probabilities + * of each letter being chosen. + * + * #define DEBUGG + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "beep.h" +/* Define USG for termio a la System V. */ +#ifdef __hpux +#define USG +#endif +/* + * Define the return type of a signal handler. Defaults to void, which is + * what most modern systems are using these days. + */ +#ifndef SIGRET +#define SIGRET void +#endif + +#define FREQUENCY 800. +#define FREQUENCY2 602. +#define VOLUME .5 +#define WORDS_PER_MINUTE 20. +#define MAX_BEHINDNESS 0 + +static int whichfrequ = 0; +static float frequency1 = FREQUENCY; +static float frequency2 = FREQUENCY2; +static float frequency; +static float volume = VOLUME; +static float dot_time; +static float dash_time; +static float intra_char_time; +static float inter_char_time; +static float inter_word_time; +static float catchup_time; +static int showletters = 0; +static int showmorse = 0; +static int wordsbefore = 0; +static int wordsafter = 0; +static int fancyending = 1; +static int noticebad = 0; +static int testing = 0; +static int showtesting = 0; +static int keepquiet = 0; +static int dynamicspeed = 0; +static int charbychar = 0; +static int tryagaincount = 1; +static float words_per_minute; +static float fwords_per_minute; +static int randomletters = 0; +#define LETMESEE 2 +static int typeaway = 0; + +static int totalhitcount = 0; +static int totalmisscount = 0; +static int helpmeflag = 0; + +#define MAXWORDLEN 20 +#define TESTBUFSZ ((MAXWORDLEN+1)*10) +static int wordlen = MAXWORDLEN; +static int wordcount = -1; +static time_t starttime; +static int timeout = -1; +static int testpointer = -1; +static int testlength = 0; +static int behindness = 0; +static int max_behindness = MAX_BEHINDNESS; +static char teststring[TESTBUFSZ]; +static int yourpointer = -1; +static int yourlength = 0; +static char yourstring[TESTBUFSZ]; +static int linepos = 0; + +/* + * How many times can a given character not be asked before + * kicking up the probability of asking that one by one randomfactor unit. + */ +#define RIPECOUNT 64 + +#define TWOFIFTYSIX 256 +static char *(code[TWOFIFTYSIX]); +static int errorlog[TWOFIFTYSIX]; +static int randomfactor[TWOFIFTYSIX]; +static int randomripe[TWOFIFTYSIX]; +extern int testterminal (); +extern int randomletter (); + +/* + * Pointers to termcap/terminfo "smso/so" (enter_standout_mode) and "rmso/se" + * (exit_standout_mode) terminal string capabilities (initialized respectively + * with ANSI sequences "\E[7m" to turn the inverse video mode on and "\E[0m" + * to turn all video attributes off). + */ +char *enter_standout_mode = "\033[7m"; +char *exit_standout_mode = "\033[0m"; + +/* + * Value of (Wrong - Right), which, if exceeded, will cause the program + * to start prompting you. Above MAX_ERROR_THRESHOLD it will never prompt. + * Don't let the user bank too much credit for past correct answers; + * limit it by min(ERROR_FLOOR, error_threshold). + */ +#define MAX_ERROR_THRESHOLD 1000 +#define ERROR_FLOOR -3 +static int error_threshold = MAX_ERROR_THRESHOLD; +static int error_floor = ERROR_FLOOR; + +/* + * How many characters behind before it decides you're having + * trouble keeping up. + */ +#define BEHIND 1 +#define WAYBEHIND 3 +#define TOOFARBEHIND 6 +/* + * If SLOWPOKE or more wpm ticks go by, then it decides you are having lots + * of trouble remembering this character, and need to be asked it more + * often. + */ +#define SLOWPOKE 10 +/* You aren't slow -- you left and came back! */ +#define SLOWPOKEMAX (50 * SLOWPOKE) +/* + * If FASTPOKE or less wpm ticks go by, then it decides you are good at this + * character, and need to be asked it less often. + */ +#define FASTPOKE 4 + +/* + * These control how quickly the dynamicspeed option acts when you are + * fast or slow. Easier to slow down than speed up! + */ +#define ERRORSLOWER 1.04 +#define ALOTSLOWER 1.15 +#define ALITTLESLOWER 1.02 +#define ALITTLEFASTER 1.02 + +/* + * How many inter_char_time's to give you to answer after the end of + * a word before considering that you are not keeping up. + * Maximum of 2.3, minimum of 0. + * The bigger the value, the easier it is to kick in the "automatic + * speedup" when using the "-d" option. The maximum means you have (almost) + * right up to the beginning of the next word to answer and still have it + * count as keeping up. + */ +#define SPORTING_RATIO 1.5 + +/* + * The bigger, the more evenly things start out. + * (Must be at least 2) + */ +#define RANDOMBASELEVEL 7 +/* + * RANDOMINCWORSE scales how badly you are punished for being wrong + * or taking too long. RANDOMINCBETTER scales how you are rewarded for + * answering quickly or being right. + */ +#define RANDOMINCWORSE 6 +#define RANDOMINCBETTER 7 +#define RANDOMMAX (30 * RANDOMBASELEVEL) +/* + * The average length of a random word (chosen using exponential distribution). + * After implementing this I'm not so sure an exponential distribution + * actually models the distribution of real word lengths in English very well. + * It's not too bad, though, and the words themselves are all garbage anyway, + * so what the heck. + */ +#define RANDWORDLEN 3.5 + +/* Put in a newline instead of a space when past this column */ +#define LINELENGTH 78 + +/* An EOF without the EOF (%) sound */ +#define SILENTEOF -2 +/* Toggle tone frequency on control-G within input file */ +#define FREQU_TOGGLE ((int)'\007') + +/* + * If you want the morse code to come out synchronized with the printing + * of dots and dashes with the -m option, then define this. The problem + * is that then the morse code sounds ratty on slower CPU's. + * John Shalamskas (KJ9U) suggested turning the precise morse-code printing + * synching off because he didn't like the resulting code quality! + */ +#undef FLUSHCODE + +/* + * Choose your favorite random number generator! + */ +#ifndef USERANDOM +#ifndef USELRAND +#ifndef USERAND +#define USERANDOM /* USELRAND or USERAND are the other choices */ +#endif +#endif +#endif + +#ifdef USERANDOM +#define RANDOM() random() +#define SEEDRANDOM(s) srandom((int)s) +#endif +#ifdef USELRAND +#define RANDOM() lrand48() +#define SEEDRANDOM(s) srand48((long)(s)) +long lrand48 (); +#endif +#ifdef USERAND +/* + * UGH, are you really sure you want to use this one? + * This one really stinks! + */ +#define RANDOM() rand() +#define SEEDRANDOM(s) srand((int)(s)) +int rand (); +#endif + +void new_words_per_minute (void); +void dowords (int c); +void morse (int c); +void show (char *s); +void testaddchar (char c); +void youraddchar (char c); +void pollyou (void); +void tone (float hertz, float duration, float amplitude); +void toneflush (void); +void openterminal (void); +int readterminal (char **string); +void closeterminal (void); +void report (void); + +SIGRET die (), suspend (); +void cleanup (); + +int +main (argc, argv) + int argc; + char **argv; +{ +extern char *optarg; +extern int optind; +int ch; +char *p; +int ii, jj; +int firsttime, notdoneyet; +int yourchar; +float randexp, randnum; +extern time_t time (); + + if (argc == 1 && isatty (fileno (stdin))) + { +/* + * SELF DOC + */ + printf ("Usage:\n"); + printf ("morse [options] < text_file\n"); + printf ("morse [options] words words words\n"); + printf ("morse [options] -r\n"); + printf ("morse [options] -i\n"); + printf ("Options:\n"); + printf ("-i Play what you type.\n"); + printf ("-I Like -i but don't turn off keyboard echoing.\n"); + printf ("-r Generate random text. Starts out slanted towards easy\n"); + printf (" letters, then slants towards ones you get wrong.\n"); + printf ("-n NUM (default %d means random length)\n", MAXWORDLEN); + printf (" Make words (groups) NUM characters long.\n"); + printf (" Valid values are between %d and %d.\n", 1, MAXWORDLEN); + printf ("-R NUM (default 0 means unlimited)\n"); + printf (" Set the total time (in minutes) to generate text.\n"); + printf ("-N NUM (default 0 means unlimited)\n"); + printf (" Set the total number of words (groups) to generate.\n"); + printf ("-C \'STRING\' (default all available characters)\n"); + printf (" Select characters to send from this STRING only.\n"); + printf ("-w words_per_minute (default %g)\n-f frequency_in_hertz (default %g)\n-v volume (zero to one, rather nonlinear, default %g)\n", + WORDS_PER_MINUTE, FREQUENCY, VOLUME); + printf ("-g alternate_frequency (default %g)\n (toggles via control-G in input FILE at a word break)\n", FREQUENCY2); + printf ("-F Farnsworth_character_words_per_minute\n"); + printf ("-e leave off the sound at the end\n"); + printf ("-c complain about illegal characters instead of just ignoring them\n"); + printf ("-b print each word before doing it\n"); + printf ("-a print each word after doing it\n"); + printf ("-l print each letter just before doing it\n"); + printf ("-m print morse dots and dashes as they sound\n"); +#ifdef FLUSHCODE + printf (" (this printing-intensive option slows the wpm down!)\n"); +#endif + printf ("-t Type along with the morse, but don't see what\n"); + printf (" you're typing (unless you make a mistake).\n"); + printf (" You are allowed to get ahead as much as you want.\n"); + printf (" If you get too far behind it will stop and resync with you.\n"); + printf (" You can force it to resync at the next word end by hitting control-H.\n"); + printf (" Hit ESC to see how you are doing, control-D to end.\n"); + printf (" (The rightmost space in the printout marks where the average is.\n"); + printf (" Farther left spaces separate off blocks of letters that are\n"); + printf (" about twice as probable as the average to occur, three times, etc.)\n"); + printf ("-T Like -t but see your characters (after they are played).\n"); + printf ("-s Stop after each character and make sure you get it right. (implies -t)\n"); + printf ("-q Quietly resyncs with your input (after you make a mistake).\n"); + printf ("-p NUM (default 0)\n"); + printf (" Make you get it right NUM times, for penance. (implies -s)\n"); + printf (" (Yes, NUM = 0 means you can sin all you want.)\n"); + printf ("-E NUM (default %d)\n", MAX_ERROR_THRESHOLD); + printf (" If your count of wrong answers minus right answers for a given character\n"); + printf (" exceeds this, the program will start prompting you.\n"); + printf (" If %d or above, it will never prompt. (implies -t)\n", MAX_ERROR_THRESHOLD); + printf ("-M NUM (default %d)\n", MAX_BEHINDNESS); + printf (" If you get more than this number of characters behind, pause until you\n"); + printf (" do your next letter. (1 behind is normal, 0 behind means never pause.)\n"); + printf (" (implies -t)\n"); + printf ("-d Dynamically speed up or slow down depending on how you are doing.\n"); + printf (" (if also -s, then -d _only speeds up_!)\n"); + printf ("\n"); + printf ("\n"); + printf ("\n"); + printf ("For the raw beginner trying to learn morse code I recommend\n"); + printf ("the following sequence:\n"); + printf ("\n"); + printf ("Start learning the alphabet:\n"); + printf (" morse -r -s -T -d -w 5 -F 15 -p 5 -E -10\n"); + printf ("Then drill drill drill:\n"); + printf (" morse -r -s -T -d -w 5 -F 15 -p 5 -E 0\n"); + printf ("Real-time drill, with hints if you really need it:\n"); + printf (" morse -r -T -d -w 5 -F 15 -M 2 -E 4\n"); + printf ("Simulated test:\n"); + printf (" QSO | morse -e -T -d -w 5 -F 15\n"); + printf ("The dreaded random-letter test:\n"); + printf (" morse -r -T -d -w 5 -F 15\n"); + printf ("Finally try for greater and greater speed:\n"); + printf (" morse -r -T -d -w 13 -F 24\n"); + printf ("\n"); +/* printf ("Written by (mostly) joe@montebello.soest.hawaii.edu\n"); */ + printf ("Please send updates/comments to joe@sep.stanford.edu\n"); + printf ("Get the latest revision from sepftp.stanford.edu before hacking!\n"); + exit (0); + } + + for (ii = 0; ii < TWOFIFTYSIX; ii++) + code[ii] = NULL; + +/* Load in the morse code code */ + code[(int) 'a'] = ".-"; + code[(int) 'b'] = "-..."; + code[(int) 'c'] = "-.-."; + code[(int) 'd'] = "-.."; + code[(int) 'e'] = "."; + code[(int) 'f'] = "..-."; + code[(int) 'g'] = "--."; + code[(int) 'h'] = "...."; + code[(int) 'i'] = ".."; + code[(int) 'j'] = ".---"; + code[(int) 'k'] = "-.-"; + code[(int) 'l'] = ".-.."; + code[(int) 'm'] = "--"; + code[(int) 'n'] = "-."; + code[(int) 'o'] = "---"; + code[(int) 'p'] = ".--."; + code[(int) 'q'] = "--.-"; + code[(int) 'r'] = ".-."; + code[(int) 's'] = "..."; + code[(int) 't'] = "-"; + code[(int) 'u'] = "..-"; + code[(int) 'v'] = "...-"; + code[(int) 'w'] = ".--"; + code[(int) 'x'] = "-..-"; + code[(int) 'y'] = "-.--"; + code[(int) 'z'] = "--.."; + + code[(int) '1'] = ".----"; + code[(int) '2'] = "..---"; + code[(int) '3'] = "...--"; + code[(int) '4'] = "....-"; + code[(int) '5'] = "....."; + code[(int) '6'] = "-...."; + code[(int) '7'] = "--..."; + code[(int) '8'] = "---.."; + code[(int) '9'] = "----."; + code[(int) '0'] = "-----"; + + /* Punctuation marks */ + code[(int) '.'] = ".-.-.-"; + code[(int) ','] = "--..--"; + code[(int) '?'] = "..--.."; + code[(int) '/'] = "-..-."; + code[(int) '-'] = "-....-"; + +#ifdef ALLPUNCTUATION + /* Not so commonly used punctuation marks */ + code[(int) ')'] = "-.--.-"; + code[(int) '\"'] = ".-..-."; + code[(int) '_'] = "..--.-"; + code[(int) '\''] = ".----."; + code[(int) ':'] = "---..."; + code[(int) ';'] = "-.-.-."; + code[(int) '$'] = "...-..-"; + code[(int) '!'] = "-.-.--"; +#endif + + /* Commonly used procedure signs ("prosigns") */ + code[(int) '+'] = ".-.-."; /* end of message */ + code[(int) '*'] = ".-..."; /* wait, stand-by */ + code[(int) '='] = "-...-"; /* (double dash) pause, break for text */ + code[(int) '('] = "-.--."; /* over-specified station only */ + code[(int) '%'] = "...-.-"; /* end of contact, known also as */ + +#ifdef ALLPROSIGNS + /* Not so commonly used procedure signs ("prosigns") */ + code[(int) '^'] = ".-.-"; /* new line, the same as :a, ae */ + code[(int) '#'] = "-...-.-";/* invite receiving station to transmit */ + code[(int) '&'] = "-.-.-"; /* attention */ + code[(int) '@'] = "...-."; /* understood */ +#endif + +#ifdef ALLINTERNATIONAL + /* Not so commonly used international extensions (ISO 8859-1) */ + code[(int) ((unsigned char)'\344')] = ".-.-"; /* :a (also for ae), the same as */ + if (code[(int) '^'] != NULL) code[(int) '^'] = NULL; + code[(int) ((unsigned char)'\340')] = ".--.-"; /* `a, oa (danish a with ring over it) */ + code[(int) ((unsigned char)'\347')] = "----"; /* ch (bar-ch) */ + code[(int) ((unsigned char)'\360')] = "..--."; /* -d (eth, overstrike d with -) */ + code[(int) ((unsigned char)'\350')] = "..-.."; /* `e */ + code[(int) ((unsigned char)'\361')] = "--.--"; /* ~n */ + code[(int) ((unsigned char)'\366')] = "---."; /* :o (also for oe) */ + code[(int) ((unsigned char)'\374')] = "..--"; /* :u (also for ue) */ + code[(int) ((unsigned char)'\376')] = ".--.."; /* ]p (thorn, overstrike ] with p) */ + code[(int) ((unsigned char)'\247')] = ".-.-.."; /* paragraph */ +#endif + + for (ii = 0; ii < TWOFIFTYSIX; ii++) + { + /* Everything starts equally fresh */ + randomripe[ii] = 0; + /* Start out assuming you know how everything sounds */ + errorlog[ii] = 0; + + if (code[ii] == NULL) + { + /* Ensures these will never be chosen */ + randomfactor[ii] = 0; + } + else + { + /* Start out favoring easy ones */ + randomfactor[ii] = RANDOMBASELEVEL - strlen (code[ii]); + if (randomfactor[ii] < 1) + randomfactor[ii] = 1; + } + } + + words_per_minute = WORDS_PER_MINUTE; + fwords_per_minute = -1.; + + while ((ch = getopt (argc, argv, "F:w:lbamf:g:v:tTdesp:riIcM:E:n:R:N:C:q")) != EOF) + switch ((char) ch) + { + case 'i': + typeaway = 1; + break; + case 'I': + typeaway = LETMESEE; + break; + case 'r': + randomletters = 1; + break; + case 'n': + sscanf (optarg, "%d", &wordlen); + if (wordlen < 1) wordlen = 1; + if (wordlen > MAXWORDLEN) wordlen = MAXWORDLEN; + break; + case 'R': + sscanf (optarg, "%d", &timeout); + timeout *= 60; + if (timeout < 1) timeout = -1; + break; + case 'N': + sscanf (optarg, "%d", &wordcount); + if (wordcount < 1) wordcount = -1; + break; + case 'C': + for (ii = 0; ii < strlen(optarg); ii++) + { + *(optarg+ii) = tolower(*(optarg+ii)); + } + for (ii = 0; ii < TWOFIFTYSIX; ii++) + { + if (code[ii] != NULL) + { + if (strchr(optarg, ii) != NULL) + { + randomfactor[ii] = RANDOMBASELEVEL - 1; + } else { + code[ii] = NULL; + randomfactor[ii] = 0; + } + } + } + break; + case 'c': + noticebad = 1; + break; + case 'e': + fancyending = 0; + break; + case 'T': + testing = 1; + showtesting = 1; + break; + case 't': + testing = 1; + break; + case 's': + charbychar = 1; + testing = 1; + break; + case 'q': + keepquiet = 1; + break; + case 'p': + charbychar = 1; + testing = 1; + sscanf (optarg, "%d", &tryagaincount); + break; + case 'M': + testing = 1; + sscanf (optarg, "%d", &max_behindness); + if (max_behindness < 1) + max_behindness = 0; + break; + case 'E': + testing = 1; + sscanf (optarg, "%d", &error_threshold); + if (error_threshold < error_floor) + error_floor = error_threshold; + break; + case 'd': + dynamicspeed = 1; + break; + case 'w': + sscanf (optarg, "%f", &words_per_minute); + break; + case 'F': + sscanf (optarg, "%f", &fwords_per_minute); + break; + case 'l': + showletters = 1; + break; + case 'b': + wordsbefore = 1; + break; + case 'a': + wordsafter = 1; + break; + case 'm': + showmorse = 1; + break; + case 'f': + sscanf (optarg, "%f", &frequency1); + break; + case 'g': + sscanf (optarg, "%f", &frequency2); + break; + case 'v': + sscanf (optarg, "%f", &volume); + if (volume < 0.) + volume = 0.; + if (volume > 1.) + volume = 1.; + break; + default: + fprintf (stderr, "Type \"morse\" without arguments to get self-doc!\n"); + exit (1); + break; + } + argc -= optind; + argv += optind; + + if (fwords_per_minute <= 0.) + fwords_per_minute = words_per_minute; + new_words_per_minute (); + + frequency = frequency1; + + if (BeepInit () != 0) + { + fprintf (stderr, "Can't access speaker.\n"); + exit (1); + } + + time(&starttime); + + signal (SIGINT, die); + signal (SIGTERM, die); + signal (SIGQUIT, die); + signal (SIGTSTP, suspend); + + if (testing || typeaway) + { + openterminal (); + } + +/* + * Do .25 seconds of silence initially to give the workstation time to + * get settled after the stress of starting this program and opening + * up everything. + */ + tone (frequency, .25, 0.); + toneflush (); + + if (typeaway) + { + testing = 0; + showtesting = 0; + charbychar = 0; + wordsbefore = 0; + wordsafter = 0; + randomletters = 0; + + notdoneyet = 1; + + while (notdoneyet) + { + pollyou (); + + for (jj = 0; jj < yourlength; jj++) + { + yourchar = yourstring[(yourpointer - yourlength + 1 + jj + TESTBUFSZ) % TESTBUFSZ]; + + /* Control-D: finished */ + if (yourchar == (int) '\004') + { + toneflush (); + notdoneyet = 0; + break; + } + + if (isspace (yourchar)) + { + if (showletters) + { + toneflush (); + printf ("%c", yourchar); + fflush (stdout); + } + + tone (frequency, inter_word_time, 0.); + + continue; + } + + morse (yourchar); + } + yourlength -= jj; + + if (timeout > 0) + if (difftime(time(NULL),starttime) > (double)timeout) + break; + } + } + else if (randomletters) + { + SEEDRANDOM (time (NULL)); + randexp = 1. / (1. - 1. / (float) (RANDWORDLEN)); + while (1) + { + if ((wordcount == 0) || timeout == 0) break; + + dowords (randomletter ()); + + if (wordlen == MAXWORDLEN) + { + /* Knock a few bits off the top so we're sure it won't overflow */ + /* Shift a few bits because the lower bits stink */ + /* Add in the time so it doesn't repeat from run to run */ + randnum = (float) ( + (((RANDOM () >> 9) + (long) (time (NULL))) >> 4) + & 0x00FFFFFF); + randnum = randnum - randexp * (int) (randnum / randexp); + if ((randnum >= 1.) && (linepos != 0)) dowords ((int) ' '); + } + } + } + else + { + if (*argv) + { + firsttime = 1; + + do + { + if (!firsttime) + { + dowords ((int) ' '); + } + else + firsttime = 0; + + if ((wordcount == 0) || timeout == 0) break; + + for (p = *argv; *p; ++p) + { + dowords ((int) *p); + if ((wordcount == 0) || timeout == 0) break; + } + if ((wordcount == 0) || timeout == 0) break; + + } while (*++argv); + } + else + { + while ((ch = getchar ()) != EOF) + { + dowords (ch); + if ((wordcount == 0) || timeout == 0) break; + } + } + } + + if (fancyending) + dowords (EOF); + else + dowords (SILENTEOF); + + fflush (stdout); + + if (testing) + { + /* + * WE'RE completely done, and YOU aren't! Force catch up. (Note if + * charbychar = YES we won't get here, since we're always caught up + * after each character as it comes out.) + */ + while (testlength > 0) + { + tone (frequency, catchup_time, 0.); + toneflush (); + testterminal (); + } + } + + /* Just to be sure! */ + toneflush (); + + if (showmorse || wordsbefore || wordsafter || showletters || showtesting) + printf ("\n"); + fflush (stdout); + + if (testing) + report (); + +/* If you make any mistakes exit with a return code! */ + cleanup (); + return (totalmisscount > 0); +} + +void +new_words_per_minute () +{ +float wtick, ftick, tick; + + tick = 60. / (words_per_minute * 50); + + /* + * In the limit as wpm goes past fwpm, Farnsworth becomes kosher PARIS + */ + if (fwords_per_minute <= words_per_minute) + ftick = 60. / (words_per_minute * 50); + else + ftick = 60. / (fwords_per_minute * 50); + + wtick = (50. * tick - 31. * ftick) / 19.; + + /* + * This time is used when the computer is waiting on you to hit a key; it + * is useful to scale the granularity with the real overrall words per + * minute. This also serves as a measuring rod to see if you are + * responding "fast enough". If you are too slow, then obviously you are + * having trouble with that character, and should be given it more OFTEN. + * Heh heh heh... + */ + catchup_time = tick; + + /* + * Things between characters and words go at the "remainder" speed, + * whatever space you need to make the sped-up Farnsworth characters come + * out with the correct overall words per minute. + */ + inter_char_time = wtick * 3.; + inter_word_time = wtick * 7.; + + /* Things within the character go at the Farnsworth speed */ + intra_char_time = ftick; + dot_time = ftick; + dash_time = ftick * 3.; +} + +static int tryingagain = 0, slowpoke = 0; + +void +dowords (c) + int c; +{ +static int wordc = 0; +static char word[MAXWORDLEN+1]; +char *wordp; +int ii; +int againcount = 0; +int are_we_repeating; + +/* + * Increment the line position counter. + * If a line gets too long, just cut it off by inserting a new line. + */ + if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) linepos++; + if (isspace (c) && ((linepos + wordlen) >= LINELENGTH)) c = '\n'; + if (c == '\n') linepos = 0; + + if (isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE) + { + if (wordc > 0) + { + if (wordcount > 0) wordcount--; + if (timeout > 0) + if (difftime(time(NULL),starttime) > (double)timeout) + timeout = 0; + + word[wordc] = '\0'; + +/* + * We have just read in a new complete word from the input, (hopefully) + * during the time of an inter-word space minus an inter-char space. + * Now let's go back and see what happened with the PREVIOUS word, + * the one that we had just finished playing. Did the user keep + * up with us? + */ +#ifdef DEBUG + fprintf (stderr, " [%d] ", behindness); +#endif + if (testing && dynamicspeed && !charbychar) + { + /* + * (If charbychar then behindness is ALWAYS 0 at this + * point...) + */ + if (behindness == 0) + { + /* You're a speed demon! Speed up a bit, then! */ + words_per_minute *= ALITTLEFASTER; + new_words_per_minute (); + } + else if (behindness > WAYBEHIND) + { + /* You're way behind! Slow way down. */ + words_per_minute /= ALOTSLOWER; + new_words_per_minute (); + } + else if (behindness > BEHIND) + { + /* You're behind! Slow down a bit. */ + words_per_minute /= ALITTLESLOWER; + new_words_per_minute (); + } + } + + +/* + * If the user was WAY too far behind stop and catch up with + * the "new" word as the first one. + */ + if (testing && (behindness > TOOFARBEHIND || helpmeflag)) + { + if (helpmeflag) + printf ("\nOK, let's restart.\n"); + else + printf ("\nYou are too far behind! Let's restart.\n"); + fflush (stdout); + + toneflush (); + + /* Flush the keyboard buffer */ + pollyou (); + + /* Forget the past */ + helpmeflag = 0; + behindness = 0; + testlength = 0; + yourlength = 0; + + /* Give the user a little rest. */ + sleep (2); + printf ("\nWPM now %d\n", (int) (words_per_minute + .5)); + fflush (stdout); + sleep (2); + printf ("\nREADY?\n"); + fflush (stdout); + sleep (1); + printf ("\nSET\n"); + fflush (stdout); + sleep (1); + printf ("\nGO!\n"); + fflush (stdout); + linepos = 0; + } + +/* + * Start treating the new word. + */ + if (wordsbefore) + { + /* Try to keep your out-of-sync text from getting swirled in */ + if (showtesting) + { + printf ("\n"); + linepos = 0; + } + + printf ("%s", word); + + if (showmorse || showletters || wordsafter || showtesting) + { + printf (" "); + for (ii = 0; ii < 16 - (wordc + 2); ii++) + { + printf (" "); + } + } + + fflush (stdout); + } + + if (testing && charbychar) + { + againcount = 0; + } + + for (wordp = word; *wordp != '\0'; wordp++) + { + tryingagain = 0; + + tryagain: + if (testing && !tryingagain && !showletters && + error_threshold < MAX_ERROR_THRESHOLD && + errorlog[(int) *wordp] > error_threshold) + { + toneflush (); + /* Give them a quick hint */ + printf ("[%c]", *wordp); + fflush (stdout); + + morse (*wordp); + + toneflush (); + if (!showmorse) + { + /* Erase the hint */ + printf ("\b\b\b \b\b\b"); + fflush (stdout); + } + } + else + { + morse (*wordp); + } + + if (testing) + { + if (charbychar) + { + toneflush (); + /* Force catchup */ + slowpoke = 0; + while (behindness > 0) + { + if (testterminal () && tryagaincount > 0) + { + /* + * OOPS! They got it WRONG! MAKE THEM TRY + * AGAIN! + */ + printf ("Try again.\n"); + linepos = 0; + /* + * Yeah I know gotos are inelegant but I + * don't feel like figuring out the "elegant" + * way to do this right now. + */ + againcount = tryagaincount - 1; + tryingagain = 1; + goto tryagain; + } + else + { + /* + * They got it right, or they didn't answer + * yet. + */ + if (behindness > 0) + { + /* + * They are STILL thinking, the + * slowpokes. Wait a bit before trying + * again. + */ + tone (frequency, catchup_time, 0.); + /* + * Keep track of how long they're taking + * to answer! + */ + if (slowpoke < SLOWPOKEMAX) + slowpoke++; + toneflush (); + } + else if (dynamicspeed && !slowpoke && !tryingagain) + { + /* + * They got it right without errors the + * first time and we didn't have to wait + * for them! A speed demon! Speed up a + * bit, then! + */ + words_per_minute *= ALITTLEFASTER; + new_words_per_minute (); + } + } + } + + /* Insufficient penance? */ + if (againcount > 0) + { + againcount--; + goto tryagain; + } + } + else + { + testterminal (); + /* + * Stop if we get more than max_behindness ahead. + * max_behindness == 0 means don't worry about them, + * they can be as far behind as they want and we + * won't stop! + */ + if (max_behindness > 0) + { + are_we_repeating = 0; + while (behindness >= max_behindness) + { +#ifdef DEBUG + fprintf (stderr, " (%d) ", behindness); +#endif + if (are_we_repeating) + { + /* + * Pause for a bit so we don't loop too + * fast + */ + tone (frequency, catchup_time, 0.); + } + else + { + are_we_repeating = 1; + } + /* Finish playing whatever we're playing */ + toneflush (); + /* And give them another chance */ + testterminal (); + } + } + } + } + } + + toneflush (); + if (testing) + testterminal (); + + if (wordsafter) + { + printf (" (%s)", word); + } + + if (wordsbefore || wordsafter || showmorse) + { + if (!showmorse && !testing && linepos) + { + if (wordsafter) + linepos += 3; + else + printf (" "); + } else { + printf ("\n"); + linepos = 0; + } + } + else if (showletters || showtesting) + { + if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) + { + if (showletters) + printf ("%c", c); + + if (showtesting) + testaddchar (c); + } + } + +/* + * WHEW! FINISHED QUEUEING THE WORD FOR PLAYING! + * Now finish up all the other sundry details... + */ + + /* Flush the output printing queue... */ + fflush (stdout); + /* + * Pause for a bit; this gives the user a sporting chance at + * catching up with us. + */ + tone (frequency, SPORTING_RATIO * inter_char_time, 0.); + toneflush (); + /* Start sounding an inter-word space */ + tone (frequency, inter_word_time - SPORTING_RATIO * inter_char_time, 0.); + + /* While that silence is playing check if the user has caught up. */ + if (testing) + testterminal (); + + /* We finished this word; reset the word character count */ + wordc = 0; + } + else if (!(wordsbefore || wordsafter || showmorse) + && + (showletters || showtesting)) + { + if (c != EOF && c != SILENTEOF && c != FREQU_TOGGLE) + { + if (showletters) + printf ("%c", c); + + if (showtesting) + testaddchar (c); + } + } + + if (c == EOF) + { + morse (EOF); + toneflush (); + } + else if (c == SILENTEOF) + { + toneflush (); + } + else if (c == FREQU_TOGGLE) + { + /* Switch to the other frequency */ + /* (Won't work from keyboard, only from a file.) */ + whichfrequ = 1 - whichfrequ; + switch (whichfrequ) + { + case 1: + frequency = frequency2; + break; + case 0: + default: + frequency = frequency1; + break; + } + } + } + else + { + word[wordc++] = c; + } + +/* + * If a word gets too long, just cut it off by inserting a space. + * Just call ourselves with the character we wish we'd gotten... + */ + if (wordc == wordlen && !(isspace (c) || c == EOF || c == SILENTEOF || c == FREQU_TOGGLE)) + dowords ((int) ' '); +} + +/* + * Don't try to test the person DURING the call into morse! + */ +void +morse (c) + int c; +{ + if ((isalpha (c) && (code[tolower(c)] == NULL)) || ((code[(int) '%'] == NULL) && ((c == EOF) || (c == '\004')))) + c = ' '; + + if (showletters) + { + if ((c == EOF) || (c == '\004')) + { + if (showmorse) + { + printf (""); + } else { + printf ("%%"); + } + } + else if (c == '.' && showmorse) + printf (""); + else if (c == '-' && showmorse) + printf (""); + else if (c == '+' && showmorse) + printf (""); + else if (c == '*' && showmorse) + printf (""); + else if (c == '=' && showmorse) + printf (""); + else if (c == '(' && showmorse) + printf (""); + else if (c == '%' && showmorse) + printf (""); +#ifdef ALLPROSIGNS + else if (c == '^' && (code[(int) '^'] != NULL) && showmorse) + printf (""); + else if (c == '#' && showmorse) + printf (""); + else if (c == '&' && showmorse) + printf (""); + else if (c == '@' && showmorse) + printf (""); +#endif + else + printf ("%c", c); + + fflush (stdout); + } + + if (isalpha (c)) + { + if (testing) + testaddchar (c - (isupper (c) ? 'A' : 'a') + 'a'); + show (code[c - (isupper (c) ? 'A' : 'a') + 'a']); + } + else if ((c == EOF) || (c == '\004')) + { + show (code[(int) '%']); + } + else if (code[c] != NULL) + { + if (testing) + testaddchar (c); + show (code[c]); + } + else + { + /* Oops! This letter is junk! */ + + if (noticebad) + { + if (showletters) + { + fflush (stdout); + } + + /* Simulate a stumble */ + tone (frequency, 2. * inter_word_time, 0.); + toneflush (); + } + + if (showletters) + { + /* Wipe out what we just printed */ + fflush (stdout); + printf ("\b"); + printf (" "); + printf ("\b"); + fflush (stdout); + } + + if (noticebad) + { + if (showletters) + { + /* And replace it with an error message */ + printf (""); + fflush (stdout); + } + + /* Give the error call */ + show ("........"); + + /* Regroup */ + tone (frequency, inter_word_time, 0.); + } + } + + if (showmorse) + printf (" "); + fflush (stdout); + toneflush (); + tone (frequency, inter_char_time - intra_char_time, 0.); +} + + +/* + * Don't try to test the person WHILE doing dots and dashes! + */ +void +show (s) + char *s; +{ +char c; + + while ((c = *s++) != '\0') + { + tone (frequency, intra_char_time, 0.); + +#ifdef FLUSHCODE + if (showmorse) + toneflush (); +#endif + + switch (c) + { + case '.': + tone (frequency, dot_time, volume); + break; + case '-': + tone (frequency, dash_time, volume); + break; + } + + if (showmorse) + { + printf ("%c", c); + fflush (stdout); +#ifdef FLUSHCODE + toneflush (); +#endif + } + } +} + +/* + * This only gets passed valid characters: ones + * that have a morse code associated with them + * or ones for which isspace(c) is true. + */ +void +testaddchar (c) + char c; +{ + testpointer = (testpointer + 1) % TESTBUFSZ; + teststring[testpointer] = c; +#ifdef DEBUG + fprintf (stderr, " (%c,%d,%d) ", c, testlength, behindness); +#endif + testlength++; + if (testlength > TESTBUFSZ) + { + fprintf (stderr, "\n\nInput buffer queue overflow! Make TESTBUFSZ bigger!\n"); + fprintf (stderr, "(Or don't fall so far behind)\n"); + + die (); + } + +/* + * Since you are never asked to type spaces (you can type them if + * you want, but they are ignored) spaces in the input file don't + * count against your "behindness". + */ + if (!isspace (c)) + behindness++; +} + +void +youraddchar (c) + char c; +{ + yourpointer = (yourpointer + 1) % TESTBUFSZ; + yourstring[yourpointer] = c; +#ifdef DEBUG + fprintf (stderr, " <%c,%d> ", c, yourlength); +#endif + yourlength++; + if (yourlength > TESTBUFSZ) + { + fprintf (stderr, "\n\nKeyboard typeahead buffer queue overflow! Make TESTBUFSZ bigger!\n"); + fprintf (stderr, "(Or don't type so far ahead... how did you expect to get them right anyway?)\n"); + + die (); + } +} + +void +pollyou () +{ +int ii, num; +char *string; + + num = readterminal (&string); + + for (ii = 0; ii < num; ii++) + youraddchar (string[ii]); +} + +int +testterminal () +{ +int testinc, yourinc; +int correctchar, yourchar, yourcharnocase; +int errorcount; +int resync; + + errorcount = 0; + +/* + * There is nothing in the input file queue right now, + * so we can't process any of your keystrokes. + * Defer processing until we can catch up with YOU! + */ + if (testlength == 0) + return errorcount; + + /* We're ready for you; but are you ready for us? */ + pollyou (); + +/* + * Process your entries and the input queue entries in parallel + */ + if (yourlength > 0 && testlength > 0) + { + for (testinc = 0, yourinc = 0; + testinc < testlength && yourinc < yourlength; + testinc++, yourinc++) + { + correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; + + /* + * The latter half of this if shouldn't be necessary, but just in + * case... + */ + if (isspace (correctchar) || code[correctchar] == NULL) + { + if (showtesting) + { + printf ("%c", correctchar); + fflush (stdout); + } + + /* White space doesn't count for "behindness" */ + behindness++; + /* The _other_ pointer wasn't used; don't increment it. */ + yourinc--; + + /* Short circuit the loop */ + continue; + } + + + yourchar = yourstring[(yourpointer - yourlength + 1 + yourinc + TESTBUFSZ) % TESTBUFSZ]; + if (isalpha (yourchar)) + yourcharnocase = yourchar - (isupper (yourchar) ? 'A' : 'a') + 'a'; + else + yourcharnocase = yourchar; + + /* Did you type something rude? If so, just ignore it. */ + if (isspace (yourchar) || code[yourcharnocase] == NULL) + { + /* ESCAPE: dump status info */ + /* Control-D: dump status info and then bye bye */ + /* Control-H: force restart */ + if (yourchar == '\033' || yourchar == (int) '\004') + { + report (); + + if (yourchar == (int) '\004') + die (); + } + else if (yourchar == '\b') + { + helpmeflag = 1; + } + + /* The _other_ pointer wasn't used; don't increment it. */ + testinc--; + /* Short circuit the loop */ + continue; + } + + if (keepquiet > 1) + { + for (resync = testinc; resync < testlength; resync++) + if ( yourcharnocase == teststring[(testpointer - testlength + 1 + resync + TESTBUFSZ) % TESTBUFSZ]) + { + for (; testinc < resync; testinc++) + { + correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; + if (isspace (correctchar) || code[correctchar] == NULL) + { + if (showtesting) printf ("%c", correctchar); + } else { + printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode); + } + } + fflush (stdout); + correctchar = teststring[(testpointer - testlength + 1 + testinc + TESTBUFSZ) % TESTBUFSZ]; + keepquiet = 1; + break; + } + } + + if (yourcharnocase != correctchar) + { + errorcount++; + totalmisscount++; + + /* + * Record that you are having trouble with these. + */ + errorlog[correctchar]++; + if (code[yourcharnocase] != NULL && + errorlog[yourcharnocase] < MAX_ERROR_THRESHOLD) + errorlog[yourcharnocase]++; + + if (keepquiet > 0) + { + printf("%s%c%s", enter_standout_mode, correctchar, exit_standout_mode); + fflush (stdout); + keepquiet++; + } else { + /* + * Scold them for their mistake. + */ + printf ("\n"); +#ifdef ERROR_VOLUME +#ifndef ERROR_FREQ +#define ERROR_FREQ 2000. +#endif + /* Beep using the tone generator */ + toneflush (); + tone ((float) (ERROR_FREQ), .1, (float) (ERROR_VOLUME)); +#else + /* Beep using control-G */ + printf ("\007"); +#endif + printf ("%c (%s) for %c (%s)\n", + yourchar, code[yourcharnocase], + correctchar, code[correctchar]); + fflush (stdout); + linepos = 0; + } + + if (charbychar) + { + /* Give them a bit of time to think about their error */ + tone (frequency, inter_word_time, 0.); + toneflush (); + } + if (dynamicspeed && !charbychar) + { + /* + * Slow down. Doesn't make sense to slow down for errors, + * though, if you've got all the time you want to think + * about each one. + */ + words_per_minute /= ERRORSLOWER; + new_words_per_minute (); + } + if (randomletters && !tryingagain) + { + /* + * Ask ones that confused you more often! + */ + if (code[yourcharnocase] != NULL) + { + randomfactor[yourcharnocase] += (3 * RANDOMINCWORSE / 2); + if (randomfactor[yourcharnocase] > RANDOMMAX) + randomfactor[yourcharnocase] = RANDOMMAX; + } + + randomfactor[correctchar] += RANDOMINCWORSE * 2; + if (randomfactor[correctchar] > RANDOMMAX) + randomfactor[correctchar] = RANDOMMAX; + } + } + else + { + /* + * Record that you got this right. + */ + if (!tryingagain) + { + totalhitcount++; + if (errorlog[correctchar] > error_floor) + errorlog[correctchar]--; + } + + if (showtesting) + { + printf ("%c", yourchar); + fflush (stdout); + } + + if (randomletters && !tryingagain) + { + if (slowpoke == SLOWPOKEMAX) + { + printf ("\nNice to have you back again, I was getting bored!\n"); + linepos = 0; + } + else if (slowpoke >= SLOWPOKE * 3) + { + /* + * Did you take too long thinking about it? If so, + * you probably need to be asked this one more + * often... + */ + randomfactor[correctchar] += (3 * RANDOMINCWORSE / 2); + if (randomfactor[correctchar] > RANDOMMAX) + randomfactor[correctchar] = RANDOMMAX; + + /* + * Hits this slow shouldn't count! You were obviously + * just guessing! (But it doesn't count as an error + * either.) + */ + totalhitcount--; + } + else if (slowpoke > FASTPOKE) + { + randomfactor[correctchar] += + (slowpoke * RANDOMINCWORSE) / (2 * SLOWPOKE); + if (randomfactor[correctchar] > RANDOMMAX) + randomfactor[correctchar] = RANDOMMAX; + } + else if (slowpoke <= (FASTPOKE / 2)) + { + /* + * Ask ones that you quickly answer correctly less + * often! + */ + randomfactor[correctchar] -= (3 * RANDOMINCBETTER / 2); + /* + * Don't let randomfactor hit 0, or you'll NEVER be + * asked this one AGAIN! + */ + if (randomfactor[correctchar] < 1) + randomfactor[correctchar] = 1; + } + else if (slowpoke <= FASTPOKE) + { + randomfactor[correctchar] -= (RANDOMINCBETTER / 2); + if (randomfactor[correctchar] < 1) + randomfactor[correctchar] = 1; + } + } + } + + } + testlength -= testinc; + behindness -= testinc; + yourlength -= yourinc; + } + +/* + * If there are some extra white space characters in the input queue + * it's OK, we'll get to them next time or we'll clean them out at the + * end. + */ + return errorcount; +} + + +/*----------------------------------------*/ + +void +tone (hertz, duration, amplitude) + float hertz, duration, amplitude; +{ + Beep ((int) (duration * 1000), (int) (amplitude * 100), (int) hertz); +} + + +void +toneflush () +{ + BeepWait (); +} + + +/*----------------------------------------*/ + +#include +#include +#ifdef USG +#include +struct termio oldtermgtty; +struct termio termgtty; +#else +#include +struct sgttyb oldtermgtty; +struct sgttyb termgtty; +#endif +static char *terminal = "/dev/tty"; +static int termfd; +static int termopen = 0; + +#ifndef USG +static int oldflgs, newflgs; +#endif + +void +openterminal () +{ + /* get parameters and open terminal */ + +#ifdef USG + termfd = open (terminal, O_RDWR | O_NDELAY, 0); + ioctl (termfd, TCGETA, &termgtty); + oldtermgtty = termgtty; + if (typeaway != LETMESEE) + termgtty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + termgtty.c_lflag &= ~ICANON; + termgtty.c_cc[VMIN] = 1; + termgtty.c_cc[VTIME] = 0; + ioctl (termfd, TCSETAW, &termgtty); +#else + termfd = open (terminal, O_RDWR, 0); + ioctl (termfd, TIOCGETP, &termgtty); + oldtermgtty = termgtty; + if (typeaway != LETMESEE) + termgtty.sg_flags &= ~ECHO; + termgtty.sg_flags |= CBREAK; + ioctl (termfd, TIOCSETP, &termgtty); + oldflgs = fcntl (termfd, F_GETFL); + newflgs = oldflgs | FNDELAY; +#endif + + termopen = 1; +} + +int +readterminal (string) + char **string; +{ +/* This must be declared static! */ +static char line[TESTBUFSZ]; +int n; + +#ifndef USG + fcntl (termfd, F_SETFL, newflgs); +#endif + n = read (termfd, line, sizeof (line) - 1); +#ifndef USG + fcntl (termfd, F_SETFL, oldflgs); +#endif + + if (n > 0) + { + line[n] = '\0'; + *string = line; + } + else + *string = NULL; + + return n; +} + +void +closeterminal () +{ +#ifdef USG + ioctl (termfd, TCSETAW, &oldtermgtty); +#else + ioctl (termfd, TIOCSETP, &oldtermgtty); +#endif + close (termfd); +} + +SIGRET +die () +{ + cleanup (); + exit (1); +} + +void +cleanup () +{ + if (termopen) + closeterminal (); + BeepCleanup (); +} + +SIGRET +suspend () +{ + signal (SIGTSTP, suspend); + cleanup (); + kill (getpid (), SIGSTOP); + if (termopen) + openterminal (); + BeepResume (); +} + + +/*----------------------------------------*/ + +int +randomletter () +{ +int ii; +int sum, sum2; +long ranspot; +extern time_t time (); +static int lasttime = -1; +static long norepeat; + +/* + * This keeps the not-so-random random number generator from ignoring + * certain characters forever! + */ + norepeat = ((long) time (NULL) / 31) % 17291; + +/* + * All the usable letters get one unit riper. + */ + for (ii = 0; ii < TWOFIFTYSIX; ii++) + { + if (randomfactor[ii] > 0) + { +#ifdef DEBUGG + fprintf (stderr, "%c: %d %d\n", + (char) ii, randomfactor[ii], randomripe[ii]); +#endif + randomripe[ii]++; + } + } + + sum = 0; + for (ii = 0; ii < TWOFIFTYSIX; ii++) + sum += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT)); + +/* + * The low bits of random aren't very random, I don't care WHAT + * the manual claims. + */ + do + { + ranspot = ((RANDOM () >> 4) % sum + norepeat) % sum; + + sum2 = 0; + for (ii = 0; ii < TWOFIFTYSIX - 1; ii++) + { + sum2 += (randomfactor[ii] + (int) (randomripe[ii] / RIPECOUNT)); + + if (sum2 > ranspot) + break; + } + /* Do it again if you got the same as last time! */ + } while (ii == lasttime); + + /* This one is FRESH again. */ + randomripe[ii] = 0; + /* Remember for next time. */ + lasttime = ii; + + return ii; +} + +void +report () +{ +int ii, jj, count; +float sum; +int randomstr[TWOFIFTYSIX]; +extern int rancomp (); + + printf ("\nCurrent words per minute: %.1f\n", words_per_minute); + + printf ("Total hits %d, misses %d", totalhitcount, totalmisscount); + if (totalmisscount > 0) + printf (", hit per miss ratio %.1f\n", (float) totalhitcount / (float) totalmisscount); + else + printf ("\n"); + + if (randomletters) + { + printf ("Most to least frequent choices:\n"); + count = 0; + sum = 0.; + for (ii = 0; ii < TWOFIFTYSIX; ii++) + { + if (randomfactor[ii] > 0) + { + sum += (randomfactor[ii] + (randomripe[ii] / (float) RIPECOUNT)); + randomstr[count] = ii; + count++; + } + } + + qsort ((char *) randomstr, count, sizeof (randomstr[0]), rancomp); + + for (ii = 0; ii < count; ii++) + { +/* + * Insert a space for each jump across an integer. + * The normalization (count/sum) ensures that if all + * letters were equally probable, they would all have value 1. + * Since they are not generally equally probable, then 1 is just the average. + * Thus the rightmost space in the printout marks where the average is. + * Further left spaces separate off blocks of letters that are approximately + * twice as probable as the average, three times, etc. + */ + if (ii > 0) + { + for (jj = 0; jj < + (int) ( + (randomfactor[randomstr[ii - 1]] + (randomripe[randomstr[ii - 1]] / (float) RIPECOUNT)) + * count / sum) - + (int) ( + (randomfactor[randomstr[ii]] + (randomripe[randomstr[ii]] / (float) RIPECOUNT)) + * count / sum); + jj++) + printf (" "); + } + printf ("%c", (char) randomstr[ii]); + } + printf ("\n"); + } + + /* + * So you don't get penalized for being "slow" after this. + */ + if (charbychar) + slowpoke = SLOWPOKEMAX + 1; + + fflush (stdout); +} + +int +rancomp (elem1, elem2) + int *elem1, *elem2; +{ +float a, b; + + a = (randomfactor[(*elem1)] + (randomripe[(*elem1)] / (float) RIPECOUNT)); + b = (randomfactor[(*elem2)] + (randomripe[(*elem2)] / (float) RIPECOUNT)); + + if (a == b) + return 0; + else if (a > b) + return -1; + else + return 1; +} diff --git a/qso/Makefile b/qso/Makefile new file mode 100644 index 0000000..fe85ef8 --- /dev/null +++ b/qso/Makefile @@ -0,0 +1,20 @@ +# Your favorite C compiler +CC = gcc +#CC = cc + +# The flags your favorite C compiler requires to work. +CFLAGS = -O3 -m486 + +OBJECTS = QSO.o put_form.o make_fre.o grammar.o util.o init_str.o +INCLUDES = antenna.h callsign.h city.h cityh.h heights.h jobs.h misc.h \ + names.h newcity.h rig.h weather.h + +all: QSO + +QSO: $(OBJECTS) + $(CC) -o $@ $(OBJECTS) + +init_str.o: $(INCLUDES) + +clean: + rm -f *.o QSO diff --git a/qso/QSO.c b/qso/QSO.c new file mode 100644 index 0000000..64d1c20 --- /dev/null +++ b/qso/QSO.c @@ -0,0 +1,742 @@ +/* + * Return-Path: + * Received: from cadillac.siemens.com by montebello.soest.hawaii.edu (4.1/montebello-MX-1.9) + * id AA01487; Mon, 10 Aug 92 03:21:41 HST + * Received: from kevin.siemens.com by cadillac.siemens.com (4.1/SMI-4.0) + * id AA25847; Mon, 10 Aug 92 09:21:37 EDT + * Date: Mon, 10 Aug 92 09:21:37 EDT + * From: pjd@cadillac.siemens.com (paul j. drongowski) + * Message-Id: <9208101321.AA25847@cadillac.siemens.com> + * To: joe@montebello.soest.hawaii.edu + * Status: RO + */ + +/* + * This copy is slightly hacked by Joe Dellinger, August 1992 + * And some more... November 1992 + * + * Partially re-written by "murray@vs6.scri.fsu.edu" + * to use better grammar and to avoid some nonsensical + * responses (like claiming to be a person who's been licensed + * longer than they've been alive!), Jan 1994 + * + * Those improvements merged with others by Joe Dellinger, Oct 1994 + */ + +/* + * Generate QSO + */ + +/* + * Author: Paul J. Drongowski / N2OQT + * Address: 1 Allen Court + * Plainsboro, New Jersey 08536 + * Date: 10 October 1991 + * + * Copyright (c) 1991 Paul J. Drongowski + */ + +/* + * When run, this program generates a single QSO. The form of the + * QSO is similar to QSO's one would expect to hear at a code test. + * It begins with a series of V's (commented out in this version), + * callsigns of the receiver and + * sender, followed by a few sentences about weather, name, + * occupation, etc. The QSO ends with the callsigns of the receiver + * and sender. + * + * All output is produced using "printf." This should make the + * program easy to port. Output can be piped into another program + * such as sparc-morse on the Sun or it can be redirected into + * a file (without viewing the contents of course!) + * + * The program design is similar to a "random poetry generator" + * or "mad-libs." Each QSO form is generated by its own C function, + * such as "PutForm1." Each function calls other C functions to + * produce the sentences in the QSO. The sentence forms are + * selected somewhat randomly as well as any blanks to be filled. + * Words and phrases are selected from several lists such as + * "Transceiver," "Antenna," "Job," etc. Sometimes this scheme is + * clever as in the formation of city names. Sometimes it is + * stupidly simple-minded and grammatical agreement is lost. + * By the way, the callsigns are real and were picked from + * rec.radio.amateur.misc on USENET. + * + * The program was constructed in C for Sun workstations. It uses + * the library function "drand48" in function "Roll" to produce + * pseudo-random numbers. The library function "srand48" and "time" + * in "main" are used to set the pseudo-random number seed. + * + * Known problems and caveats? Hey, it`s software! All Morse + * training programs handle the procedural signs (e.g., AR, SK) + * differently. The function "PutQSO" currently prints "+ %" + * for the AR and SK at the end of the QSO. These may be ignored, + * mapped into something else, or just plain cause your training + * program to roll over and play dead. I don`t know. This is a + * cheap hack. + * + * And speaking of cheap... The program will not generate all + * characters and pro-signs that are found on an "official" code + * test. This program is for practice only and should be supplemented + * with lots of random code. + * + * Please note that I retain the copyright + * to this program. You may use it for any non-commercial purposes. + * This is roughly equivalent to "fair use." + * + * Always have fun! + */ + + +#include +/* +#include + */ +#include + +char *A_Or_An (); + +int License_Seed (); + +int their_age; /* PERSON'S AGE, SO THEY AREN'T LICENSED MORE + * THAN THEIR AGE */ + + +extern char *Transceiver[]; +extern char *Antenna[]; +extern char *UpFeet[]; +extern char *Weather1[]; +extern char *Weather2[]; +extern char *Power[]; +extern char *Job[]; +extern char *Name[]; +extern char *CallSign[]; +extern char *License[]; +extern char *City[]; +extern char *NewCity[]; +extern char *CityHeights[]; +extern char *New[]; +extern char *Heights[]; +extern char *State[]; +extern char *Frqmisc[]; +extern char *Callmisc[]; +extern char *NumMisc[]; +extern char *Miscellaneous[]; +extern char *RST[]; + +int NXCVR; +int NANTENNA; +int NUPFEET; +int NWX1; +int NWX2; +int NPOWER; +int NJOB; +int NNAME; +int NCALLSIGN; +char *Sender, *Receiver; +int NLICENSE; +int NCITY; +int NNEWCITY; +int NCITYHTS; +int NNEW; +int NHEIGHTS; +int NSTATE; +int NFRQMISC; +int NFRQCALLMISC; + +char *FrqCallmisc[] = +{ + "QRW %s %d", + "QRZ %s %d", + "QSN %s %d", + "QSX %s %d", + "QRW %s %d?", + "QRZ %s %d?", + "QSN %s %d?", + "QSX %s %d?", + 0 +}; + +char *Choose (); +int NCALLMISC; +int NNUMMISC; +int NMISC; +int NRST; + + +/* + ************************************* + * Routines to put sentences/clauses * + ************************************* + */ + +void +PutMisc () +{ + printf ("%s\n", Choose (Miscellaneous, NMISC)); +} + +void +PutThanks () +{ + switch (Roll (6)) + { + case 2: + printf ("Thanks for your call.\n"); + break; + + case 3: + printf ("Tnx for ur call.\n"); + break; + + case 4: + printf ("Tnx for the call.\n"); + break; + + case 5: + printf ("Thanks for the call.\n"); + break; + + default: + printf ("Thanks %s for the call.\n", Choose (Name, NNAME)); + break; + } +} + +void +PutName () +{ + switch (Roll (6)) + { + case 2: + printf ("Name is %s.\n", Choose (Name, NNAME)); + break; + + case 4: + printf ("This is %s.\n", Choose (Name, NNAME)); + break; + + case 5: + printf ("%s here.\n", Choose (Name, NNAME)); + break; + + default: + printf ("My name is %s.\n", Choose (Name, NNAME)); + break; + } +} + +void +PutJob () +{ + switch (Roll (20)) + { + case 2: + case 3: + printf ("My occupation is %s.\n", Choose (Job, NJOB)); + break; + + case 4: + case 5: + printf ("I work as %s.\n", A_Or_An (Choose (Job, NJOB))); + break; + + case 6: + printf ("I was %s, now unemployed.\n", A_Or_An (Choose (Job, NJOB))); + break; + + case 11: + case 12: + printf ("Occupation is %s.\n", Choose (Job, NJOB)); + break; + + default: + printf ("I am %s.\n", A_Or_An (Choose (Job, NJOB))); + break; + } +} + +void +PutAge () +{ + + their_age = Roll (60) + 16; + + switch (Roll (5)) + { + case 3: + printf ("My age is %d.\n", their_age); + break; + + case 4: + printf ("I am %d years old.\n", their_age); + break; + + default: + printf ("Age is %d.\n", their_age); + break; + } +} + +void +PutLicense () +{ +int get_years_licence; + + get_years_licence = Roll (License_Seed ()); + if (get_years_licence < 2) + get_years_licence = 2; + + switch (Roll (12)) + { + case 1: + printf ("I have %s class licence.\n", + A_Or_An (Choose (License, NLICENSE))); + break; + + case 2: + printf ("I am %s license ham.\n", + A_Or_An (Choose (License, NLICENSE))); + break; + + case 3: + printf ("I am %s licence ham.\n", + A_Or_An (Choose (License, NLICENSE))); + break; + + case 4: + printf ("I have been licenced %d years as %s class.\n", + get_years_licence, Choose (License, NLICENSE)); + break; + + case 5: + printf ("I have %s class license.\n", + A_Or_An (Choose (License, NLICENSE))); + break; + + case 6: + printf ("I am %s class ham.\n", + A_Or_An (Choose (License, NLICENSE))); + break; + + case 7: + printf ("I have been licensed %d years as %s class.\n", + get_years_licence, Choose (License, NLICENSE)); + break; + + default: + printf ("I have been %s class ham for %d years.\n", + A_Or_An (Choose (License, NLICENSE)), get_years_licence); + break; + } +} + +void +PutTemperature () +{ + printf ("Temperature is %d.\n", Roll (80) + 10); +} + +void +PutWeather1 () +{ + switch (Roll (17)) + { + case 2: + printf ("WX is %s.\n", Choose (Weather1, NWX1)); + PutTemperature (); + break; + + case 3: + printf ("Weather here is %s.\n", Choose (Weather1, NWX1)); + break; + + case 4: + printf ("Weather is %s.\n", Choose (Weather1, NWX1)); + break; + + case 5: + printf ("WX is %s.\n", Choose (Weather1, NWX1)); + break; + + case 6: + PutTemperature (); + printf ("Weather here is %s.\n", Choose (Weather1, NWX1)); + break; + + case 7: + PutTemperature (); + printf ("Weather is %s.\n", Choose (Weather1, NWX1)); + break; + + case 8: + PutTemperature (); + printf ("WX is %s.\n", Choose (Weather1, NWX1)); + break; + + case 9: + printf ("Weather here is %s and temperature is %d.\n", + Choose (Weather1, NWX1), Roll (80) + 10); + break; + + case 10: + printf ("Weather is %s, temperature %d.\n", + Choose (Weather1, NWX1), Roll (80) + 10); + break; + + case 11: + printf ("WX is %d degrees and %s.\n", + Roll (80) + 10, Choose (Weather1, NWX1)); + break; + + case 12: + printf ("The WX is %s and the temp is %d degrees.\n", + Choose (Weather1, NWX1), Roll (80) + 10); + break; + + case 14: + printf ("Weather is %s.\n", Choose (Weather1, NWX1)); + PutTemperature (); + break; + + case 15: + printf ("Weather here is %s.\n", Choose (Weather1, NWX1)); + PutTemperature (); + break; + + default: + printf ("WX is %s and %d degrees.\n", + Choose (Weather1, NWX1), Roll (80) + 10); + } +} + +void +PutWeather2 () +{ + + switch (Roll (10)) + { + case 0: + printf ("It is %s.\n", Choose (Weather2, NWX2)); + break; + + case 1: + printf ("It is %s and %d degrees.\n", + Choose (Weather2, NWX2), Roll (80) + 10); + break; + + case 2: + printf ("The WX is %s and the temp is %d degrees.\n", + Choose (Weather2, NWX2), Roll (80) + 10); + break; + + case 3: + printf ("WX is %s and the temp is %d degrees.\n", + Choose (Weather2, NWX2), Roll (80) + 10); + break; + + case 4: + printf ("It is %s today.\n", Choose (Weather2, NWX2)); + break; + + case 5: + printf ("It is %s and %d degrees.\n", + Choose (Weather2, NWX2), Roll (100) + 3); + break; + + case 6: + printf ("The WX is %s and the temp is %d degrees.\n", + Choose (Weather2, NWX2), Roll (90) + 10); + break; + + case 7: + printf ("WX is %s and the temp is %d degrees.\n", + Choose (Weather2, NWX2), Roll (80) + 10); + break; + + default: + printf ("It is %s here.\n", Choose (Weather2, NWX2)); + break; + } +} + +void +PutWeather () +{ + switch (Roll (4)) + { + case 3: + PutWeather1 (); + break; + + default: + PutWeather2 (); + break; + } +} + +void +PutCityState () +{ + switch (Roll (6)) + { + case 4: + printf ("%s %s, ", + Choose (CityHeights, NCITYHTS), Choose (Heights, NHEIGHTS)); + break; + + case 5: + printf ("%s %s, ", Choose (New, NNEW), Choose (NewCity, NNEWCITY)); + break; + + default: + printf ("%s, ", Choose (City, NCITY)); + break; + } + printf ("%s.\n", Choose (State, NSTATE)); +} + +void +PutLocation () +{ + + switch (Roll (5)) + { + case 3: + printf ("My QTH is "); + break; + + case 4: + printf ("My location is "); + break; + + default: + printf ("QTH is "); + break; + + } + PutCityState (); +} + +void +PutRig () + +{ + switch (Roll (19)) + { + case 0: + case 1: + printf ("My rig runs %s watts into %s up %s feet.\n", + Choose (Power, NPOWER), A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + + case 2: + case 3: + printf ("Rig is a %s watt %s and antenna is %s.\n", + Choose (Power, NPOWER), Choose (Transceiver, NXCVR), + A_Or_An (Choose (Antenna, NANTENNA))); + break; + + case 4: + case 5: + printf ("My transceiver is %s.\n", A_Or_An (Choose (Transceiver, NXCVR))); + printf ("It runs %s watts into %s.\n", + Choose (Power, NPOWER), A_Or_An (Choose (Antenna, NANTENNA))); + break; + + case 6: + case 7: + printf ("The rig is %s running %s watts.\n", + A_Or_An (Choose (Transceiver, NXCVR)), Choose (Power, NPOWER)); + printf ("Antenna is %s up %s m.\n", + A_Or_An (Choose (Antenna, NANTENNA)), Choose (UpFeet, NUPFEET)); + break; + + case 8: + case 9: + case 10: + case 11: + printf ("My rig runs %s watts into %s up %s meters.\n", + Choose (Power, NPOWER), A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + + case 12: + printf ("My rig runs %s watts into %s up %s feet, but\nthe antenna has partly fallen.\n", + Choose (Power, NPOWER), A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + + case 13: + printf ("Rig is %s running %s watts into %s up %s ft.\n", + A_Or_An (Choose (Transceiver, NXCVR)), Choose (Power, NPOWER), + A_Or_An (Choose (Antenna, NANTENNA)), Choose (UpFeet, NUPFEET)); + break; + + case 14: + printf ("My rig runs %s watts into %s up %s feet.\n", + Choose (Power, NPOWER), A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + + case 15: + printf ("Rig is %s watt %s and antenna is %s.\n", + A_Or_An (Choose (Power, NPOWER)), + Choose (Transceiver, NXCVR), + Choose (Antenna, NANTENNA)); + break; + + case 16: + printf ("My transceiver is %s.\n", + A_Or_An (Choose (Transceiver, NXCVR))); + printf ("It runs %s watts into %s.\n", + Choose (Power, NPOWER), + A_Or_An (Choose (Antenna, NANTENNA))); + break; + + case 17: + printf ("The rig is %s running %s watts.\n", + A_Or_An (Choose (Transceiver, NXCVR)), + Choose (Power, NPOWER)); + printf ("Antenna is %s up %s feet.\n", + A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + + default: + printf ("Rig is %s ", + A_Or_An (Choose (Transceiver, NXCVR))); + printf ("running %s watts into %s up %s feet.\n", + Choose (Power, NPOWER), + A_Or_An (Choose (Antenna, NANTENNA)), + Choose (UpFeet, NUPFEET)); + break; + } +} + +void +PutRST () +{ +register char *TheRST; + + TheRST = Choose (RST, NRST); + + switch (Roll (8)) + { + case 0: + printf ("UR RST %s=%s.\n", TheRST, TheRST); + break; + + case 1: + printf ("RST is %s=%s.\n", TheRST, TheRST); + break; + + case 2: + printf ("RST %s=%s.\n", TheRST, TheRST); + break; + + case 3: + printf ("Your RST %s=%s.\n", TheRST, TheRST); + break; + + case 4: + printf ("Your RST is %s=%s.\n", TheRST, TheRST); + break; + + case 5: + printf ("Your signal is RST %s/%s.\n", TheRST, TheRST); + break; + + case 6: + printf ("UR signal is RST %s,%s.\n", TheRST, TheRST); + break; + + default: + printf ("Your RST is %s/%s.\n", TheRST, TheRST); + break; + } +} + + +void +PutQ_And_Freq () +{ + switch (Roll (8)) + { + case 2: + printf (Frqmisc[Roll (NFRQMISC)], make_freq ()); + printf ("\n"); + break; + + case 3: + printf (Callmisc[Roll (NFRQMISC)], + Choose (CallSign, NCALLSIGN)); + printf ("\n"); + break; + + case 4: + printf (FrqCallmisc[Roll (NFRQCALLMISC)], + Choose (CallSign, NCALLSIGN), + make_freq ()); + printf ("\n"); + break; + + case 5: + printf (NumMisc[Roll (NNUMMISC)], + Roll (3) + Roll (2) + 1); + printf ("\n"); + break; + + default: + break; + + } +} + +void +PutFirstCallsign () +{ + Sender = Choose (CallSign, NCALLSIGN); + Receiver = Choose (CallSign, NCALLSIGN); + printf ("%s de %s\n", Receiver, Sender); +} + +void +PutLastCallsign () +{ + printf ("%s de %s\n", Receiver, Sender); +} + +main () +{ + + NXCVR = CountStrings (Transceiver); + NANTENNA = CountStrings (Antenna); + NUPFEET = CountStrings (UpFeet); + NPOWER = CountStrings (Power); + NRST = CountStrings (RST); + NWX1 = CountStrings (Weather1); + NWX2 = CountStrings (Weather2); + NJOB = CountStrings (Job); + NNAME = CountStrings (Name); + NSTATE = CountStrings (State); + NCITY = CountStrings (City); + NCITYHTS = CountStrings (CityHeights); + NNEW = CountStrings (New); + NHEIGHTS = CountStrings (Heights); + NNEWCITY = CountStrings (NewCity); + NLICENSE = CountStrings (License); + NMISC = CountStrings (Miscellaneous); + NCALLSIGN = CountStrings (CallSign); + NFRQMISC = CountStrings (Frqmisc); + NFRQCALLMISC = CountStrings (FrqCallmisc); + NCALLMISC = CountStrings (Callmisc); + NNUMMISC = CountStrings (NumMisc); + + srand48 ((long) time (0)); + + PutQSO (); + + return 0; +} diff --git a/qso/antenna.h b/qso/antenna.h new file mode 100644 index 0000000..1452605 --- /dev/null +++ b/qso/antenna.h @@ -0,0 +1,33 @@ +"10 meter dipole", +"10 meter whip", +"3 element beam", +"5 band vertical", +"5 element loop", +"7 band beam", +"beam", +"delta loop", +"dipole", +"doublet zepp", +"dummy load", +"ground=plane", +"half wave dipole", +"inverted V", +"light bulb", +"log periodic", +"long=wire", +"monobander", +"parasitic beam", +"quad loop", +"quad vertical", +"quagi", +"quarter wave vertical", +"random wire up 2 kite", +"random wire up to kite", +"random wire", +"rhombic", +"symmetrical delta loop", +"trap doublet", +"tribander", +"whip", +"yagi", +"zepp", diff --git a/qso/callsign.h b/qso/callsign.h new file mode 100644 index 0000000..ed69af0 --- /dev/null +++ b/qso/callsign.h @@ -0,0 +1,108 @@ +"AA0ET", +"AA4LR", +"AA5BT", +"AA6NP", +"AA6YD", +"AA9BK", +"AB5AP", +"AB6FI", +"AC4HF", +"G0GWA", +"GM4ZNX", +"I5FLN", +"IK5AAX", +"JA1BLV", +"JY1", +"K2WK", +"K5RC", +"K6OCK", +"K6XO", +"K9ALD", +"KA0WCH", +"KA1AXY", +"KA1CV", +"KA1UTU", +"KA1ZGC", +"KA2RAF", +"KA6S", +"KB0CY", +"KB2NRH", +"KB6CSP", +"KB6JXT", +"KC6SKV", +"KC6SSS", +"KC6TDR", +"KC6VWV", +"KD3FU", +"KD4AUS", +"KD4CPL", +"KE2TP", +"KE4ZV", +"KF6BZF", +"KF8NH", +"KJ6NN", +"KJ9U", +"KJ9U/KH6", +"KK6JQ", +"KM3T", +"KT7H", +"N1AL", +"N1JCX", +"N4PBK", +"N4VRN", +"N5IAL", +"N5OP", +"N6BIS", +"N6MWC", +"N6TTO", +"N8EMR", +"N9FZX", +"N9JRV", +"N9LFF", +"N9MYI", +"NE3X", +"NH6EG", +"NH6EG/5", +"NH6ZW", +"NJ7E", +"NR3Z", +"NT1G", +"OD5NG", +"OH3BK", +"OH8NUP", +"TG9VT", +"UT5RP", +"VE6MGS", +"VE7EMD", +"VK2BQS", +"VK2EG", +"W0PBV", +"W0RIJ", +"W0RSB", +"W2FG", +"W2JGR", +"W3GRG", +"W3OTC", +"WA1UXA", +"WA2AGE", +"WA3EEC", +"WA3TBL", +"WA3UQV", +"WA5RPF", +"WA6DGX", +"WA7VYJ", +"WA8ZGO", +"WB2CJL", +"WB4JCM", +"WB5FDP", +"WB5FKH", +"WB5NRN", +"WB7CJO", +"WB7EEL", +"WB7TZA", +"WB8EOH", +"WB9IVR", +"WD0EIB", +"WH6UR", +"WH6UR/5", +"WX9T", diff --git a/qso/city.h b/qso/city.h new file mode 100644 index 0000000..b3c74b9 --- /dev/null +++ b/qso/city.h @@ -0,0 +1,179 @@ +"Agassiz", +"Aiea", +"Alexander", +"Asbury", +"Baker", +"Bayaney", +"Beckley", +"Bedford", +"Bixby", +"Branyan", +"Brazoria", +"Broken Arrow", +"Brunswick", +"Bryan", +"Burnsville", +"Buzzard", +"Cambridge", +"Carrizo", +"Catoosa", +"Chamizal", +"Charleston", +"Chepiwanoxet", +"Chesuncook", +"Chinquapin", +"Circleville", +"Claquato", +"Clarksburg", +"Clarksville", +"Clear Lake", +"Cleveland", +"College", +"Crystal", +"Duncanville", +"Elizabeth", +"Enid", +"Ewa", +"Faalaaga", +"Fairbanks", +"Fairfax", +"Fairfield", +"Flamboyant Gardens", +"Flint", +"Gahanna", +"Gappas", +"Glenpool", +"Grant", +"Greensburg", +"Guyandotte", +"Guyanoga", +"Harper Valley", +"Harper", +"Harquahala", +"Hells Crossing", +"Hill Valley", +"Hillsdale", +"Honolulu", +"Hyannis", +"Iraan", +"Iyanbito", +"Jamestown", +"Jefferson", +"Jenks", +"Juncos", +"Kaaawa", +"Kohlsaat", +"Kona", +"Laager", +"Lakewood", +"Lancaster", +"Lawrenceville", +"Leyan", +"Lincoln", +"Litchfield", +"London", +"Long Branch", +"Luxello", +"Lyndale", +"Lyndhurst", +"Lynnville", +"Maaloon", +"Maitwan", +"Manasquan", +"Mansfield", +"Maple", +"Meadows", +"Melbourne", +"Mentor", +"Mercer", +"Mesquite", +"Midland", +"Milldale", +"Milltown", +"Moorestown", +"Moscow", +"Mountain View", +"Moxee", +"Murray", +"Muskogee", +"Musquiz", +"Naxera", +"New London", +"Newfield", +"Newport", +"Nipgen", +"Nisqually", +"Nome", +"Noname", +"Noxubee", +"Nutzotin", +"Oak", +"Occoquan", +"Olmstead", +"Owasso", +"Oxford", +"Oxnard", +"Paradise", +"Paris", +"Pattaquattic", +"Pawtuxet", +"Perry", +"Piscataquis", +"Potter", +"Pottsville", +"Praag", +"Quanah", +"Quebec", +"Redwood", +"Rueechayaam", +"Russellville", +"Salem", +"San Pasqual", +"Sandy", +"Sapulpa", +"Saratoga", +"Sequatchie", +"Smithville", +"Snoqualmie", +"Springdale", +"Springfield", +"Squaxin", +"Starkville", +"Sunnyvale", +"Sunset Valley", +"Suquamish", +"Tahlequah", +"Tahquamenon", +"Texoma", +"Texum", +"Tinker", +"Trenton", +"Tulsa", +"Twisp", +"Umpqua", +"Union", +"Uruguay", +"Utica", +"Vancouver", +"Verona", +"Viroqua", +"Vista", +"Waldberggaard", +"Walnut", +"Warners Landing", +"Warren", +"Warsaw", +"Washington", +"Wesleyanna", +"Weston", +"Wheatfield", +"Williamson", +"Worchester", +"Wyandanch", +"Xanthus", +"Xenia", +"Yangtze", +"Yellowstone", +"Yreka", +"Zolton", +"Zuni", diff --git a/qso/cityh.h b/qso/cityh.h new file mode 100644 index 0000000..fa1979b --- /dev/null +++ b/qso/cityh.h @@ -0,0 +1,46 @@ +"Apple", +"Ashford", +"Baker", +"Baldwin", +"Banner", +"Barnard", +"Benton", +"Cabbot", +"Carson", +"Chester", +"College", +"Conway", +"Cornwall", +"Crystal", +"Duxbury", +"Fletcher", +"Franklin", +"Granite", +"Grant", +"Guyan", +"Hamilton", +"Harper", +"Hickory", +"Hoosick", +"Jefferson", +"Lincoln", +"Maple", +"Mercer", +"Morgan", +"Morris", +"Murray", +"Oak", +"Orwell", +"Oswego", +"Perry", +"Potter", +"Quail", +"Salem", +"Stafford", +"Stone", +"Tinker", +"Walnut", +"Warren", +"Washington", +"Weston", +"Wilton", diff --git a/qso/grammar.c b/qso/grammar.c new file mode 100644 index 0000000..173309d --- /dev/null +++ b/qso/grammar.c @@ -0,0 +1,32 @@ +int +is_vowel (first_char) + char first_char; +{ + if ( + first_char == 'A' || + first_char == 'E' || + first_char == 'I' || + first_char == 'O' || + first_char == 'U' || + first_char == 'a' || + first_char == 'e' || + first_char == 'i' || + first_char == 'o' || + first_char == 'u' + ) + return (1); + return (0); +} + +char buffer[200]; + +char * +A_Or_An (string) + char *string; +{ + if (is_vowel (string[0]) == 1) + sprintf (buffer, "an %s", string); + else + sprintf (buffer, "a %s", string); + return ((char *) buffer); +} diff --git a/qso/heights.h b/qso/heights.h new file mode 100644 index 0000000..5b7788c --- /dev/null +++ b/qso/heights.h @@ -0,0 +1,37 @@ +"Beach", +"Canyon", +"Castle", +"City", +"Corner", +"Cove", +"Creek", +"Estates", +"Forest", +"Gardens", +"Glen", +"Grove", +"Harbor", +"Heights", +"Hill", +"Hills", +"Hole", +"Island", +"Knob", +"Lake", +"Meadow", +"Meadows", +"Mill", +"Mountain", +"Neck", +"Palisades", +"Park", +"Pass", +"Plantation", +"Ridge", +"River", +"Springs", +"Station", +"Swale", +"Terrace", +"Valley", +"Wood", diff --git a/qso/init_str.c b/qso/init_str.c new file mode 100644 index 0000000..ae69660 --- /dev/null +++ b/qso/init_str.c @@ -0,0 +1,266 @@ +char *Transceiver[] = +{ +#include "rig.h" + 0 +}; + +char *Antenna[] = +{ +#include "antenna.h" + 0 +}; + +char *UpFeet[] = +{ + "10", "15", "20", "25", "30", "35", "40", "45", + "50", "55", "60", "65", "70", "75", "80", "85", + "90", "95", "100", + "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", + "10 1/2", "11 1/2", "25", "29 1/2", "30", "35", "40", "42", + "45", "45 1/2", "45.5", + "50", "55", "60", "62", "65", "70", "75", "80", "85", "87 1/3", + "90", "95", "100", "103", "137", "189 1/2", "210", "246", "250", + 0 +}; + +char *Weather1[] = +{ + "sunny", + "rain", + "freezing rain", + "sleet", + "snow", + "cloudy", + "partly cloudy", + "partly sunny", + "clear", + 0 +}; + +char *Weather2[] = +{ +#include "weather.h" + 0 +}; + +char *Power[] = +{ + "2", "5", "10", "20", "25", "40", "50", "80", "100", + "125", "140", "150", "170", "200", "250", "270", "300", + 0 +}; + +char *Job[] = +{ +#include "jobs.h" + 0 +}; + +char *Name[] = +{ +#include "names.h" + 0 +}; + +char *CallSign[] = +{ +#include "callsign.h" + 0 +}; + +char *License[] = +{ + "Novice", + "Technician", + "Tech", + "Tech plus", + "Tech plus", + "Tech plus", + "Tech plus", + "Tech no code", + "General", + "Advanced", + "Extra", + "Extra", + "Extra", + 0 +}; + +char *City[] = +{ +#include "city.h" + 0 +}; + +char *NewCity[] = +{ +#include "newcity.h" + 0 +}; + +char *CityHeights[] = +{ +#include "cityh.h" + 0 +}; + +char *New[] = +{ + "New", + "Old", + "North", + "South", + "East", + "West", + "New", + "Old", + "North", + "Northeast", + "Northwest", + "South", + "Southwest", + "Southeast", + "East", + "West", + "Upper", + "Lower", + 0 +}; + +char *Heights[] = +{ +#include "heights.h" + 0 +}; + +char *State[] = +{ + "Alabama", + "Alaska", + "Arizona", + "Arkansas", + "California", + "Colorado", + "Connecticut", + "District of Columbia", + "Delaware", + "Florida", + "Georgia", + "Guam", + "Hawaii", + "Idaho", + "Illinois", + "Indiana", + "Iowa", + "Kansas", + "Kentucky", + "Louisiana", + "Maine", + "Maryland", + "Massachusetts", + "Michigan", + "Midway", + "Minnesota", + "Mississippi", + "Missouri", + "Montana", + "Nebraska", + "Nevada", + "New Hampshire", + "New Jersey", + "New Mexico", + "New York", + "North Carolina", + "North Dakota", + "Ohio", + "Oklahoma", + "Oregon", + "Pennsylvania", + "Puerto Rico", + "Rhode Island", + "Saipan", + "American Samoa", + "South Carolina", + "South Dakota", + "Tennessee", + "Texas", + "Utah", + "Vermont", + "Virginia", + "Virgin Islands", + "Wake Island", + "Washington", + "West Virginia", + "Wisconsin", + "Wyoming", + 0 +}; + +char *Frqmisc[] = +{ + "QSN %d?", + "QSU %d?", + "QSW %d?", + "QSN %d", + "QSU %d", + "QSW %d", + 0 +}; + +char *Callmisc[] = +{ + "QRK %s", + "QRL %s", + "QRZ %s", + "QSP %s", + "QRK %s?", + "QRL %s?", + "QRZ %s?", + "QSP %s?", + 0 +}; + +char *NumMisc[] = +{ + "QRI %d", + "QRK %d", + "QRM %d", + "QRN %d", + "QRS %d", + "QRY %d", + "QSG %d", + "QTA %d", + "QTC %d", + "QRI %d?", + "QRK %d?", + "QRM %d?", + "QRN %d?", + "QRS %d?", + "QRY %d?", + "QSG %d?", + "QTA %d?", + "QTC %d?", + 0 +}; + +char *Miscellaneous[] = +{ +#include "misc.h" + 0 +}; + +char *RST[] = +{ + "555", + "577", + "578", + "579", + "588", + "589", + "599", + "478", + "354", + "248", + "126", + 0 +}; diff --git a/qso/jobs.h b/qso/jobs.h new file mode 100644 index 0000000..6605174 --- /dev/null +++ b/qso/jobs.h @@ -0,0 +1,71 @@ +"administrator", +"airline pilot", +"artist", +"attorney", +"bricklayer", +"bus driver", +"carpenter", +"chemist", +"clerk", +"clown", +"college student", +"consultant", +"cook", +"corporate liquidator", +"curator", +"dentist", +"designer", +"director", +"doctor", +"driver", +"electrician", +"engineer", +"farmer", +"fireman", +"gardener", +"geophysicist", +"guard", +"high school student", +"hs student", +"human resource consultant", +"investment banker", +"janitor", +"jr college student", +"juggler and unicyclist", +"junior college student", +"lawyer", +"letter carrier", +"librarian", +"mafia killer", +"mathematician", +"mechanic", +"midwife", +"milkman", +"nurse", +"orthodontist", +"pacifist", +"painter", +"petroleum engineer", +"physician", +"physicist", +"police officer", +"politician", +"prison guard", +"professor", +"programmer", +"researcher", +"sailor", +"sanitation worker", +"sculptor", +"secretary", +"security guard", +"semiotician", +"soldier", +"sql programmer", +"stock broker", +"student", +"systems analyst", +"teacher", +"teller", +"vulcanologist", +"writer", diff --git a/qso/make_fre.c b/qso/make_fre.c new file mode 100644 index 0000000..baa9c93 --- /dev/null +++ b/qso/make_fre.c @@ -0,0 +1,28 @@ +#define M80 2 +#define M40 3 +#define M15 4 +#define M10 5 +#define NUM_BAND 4 + +int +make_freq () +{ + switch (Roll (NUM_BAND + 2)) + { + case M80: + return (3675 + Roll (50)); + break; + case M40: + return (7100 + Roll (50)); + break; + case M15: + return (21100 + Roll (100)); + break; + case M10: + return (28100 + Roll (200)); + break; + default: + return (7100 + Roll (50)); + break; + } +} diff --git a/qso/misc.h b/qso/misc.h new file mode 100644 index 0000000..e10b2ce --- /dev/null +++ b/qso/misc.h @@ -0,0 +1,293 @@ +"+%=x/,? 73 just testing you.", +",?/=+9% 73 just testing you.", +",?/=q+% 73 just testing u.", +"8y67fvgb oops cat on keyboard.", +"9p0ojgbhnw23 oops cat on keyboard.", +"%w=+/,? 73 just testing you.", +"A clumsy gecko just fell on my hand.", +"Are you a communist?", +"Are you a democrat?", +"Are you a libertarian?", +"Are you a republican?", +"Are you divorced?", +"Are you good at auto repair?", +"Are you good at calculus? I have a question for you.", +"Are you good at car repair? I have a question for you.", +"Are you good at group theory? I have a question for you.", +"Are you good at summing infinite series?", +"Are you married?", +"Are you on daylight savings time?", +"Are you single?", +"Are you using a linear amplifier?", +"Are you using an iambic key?", +"Are you widowed?", +"Aye, laddie, have you ever been to sea?", +"Can you pick kryptonite locks? Those are tricky.", +"Can you pick kryptonite locks?", +"Can you pick master locks? Those are supposed to be easy.", +"Can you think of something else interesting to say?", +"Copy?", +"Cpy?", +"Did you attend ORU?", +"Did you hear news?", +"Did you hear the news?", +"Do u understand English?", +"Do you have a baby?", +"Do you have a cat?", +"Do you have a dog?", +"Do you have a ferret?", +"Do you have a gecko?", +"Do you have a horse?", +"Do you know morse code?", +"Do you know the word copacetic?", +"Do you know the word garrulous?", +"Do you know the word prosy?", +"Do you know what a pangram is?", +"Do you like bach?", +"Do you like bernstein?", +"Do you like c and w music?", +"Do you like classical music?", +"Do you like country music?", +"Do you like hawaiian music?", +"Do you like jawaiian music?", +"Do you like modern rock?", +"Do you like mozart?", +"Do you like pop music?", +"Do you like reggae music?", +"Do you like rock music?", +"Do you like swing music?", +"Do you listen to Deutsche Welle?", +"Do you listen to Radio Australia?", +"Do you listen to Radio Moscow?", +"Do you listen to Radio New Zealand Intl?", +"Do you listen to Radio Peace Peoples Republic of Korea?", +"Do you listen to VOA?", +"Do you listen to click and clack on PBS?", +"Do you listen to the BBC?", +"Do you prefer metric units?", +"Do you recycle often?", +"Do you understand English?", +"Do you watch Quantum Leap?", +"Do you watch STTNG?", +"Do you watch Simpsons?", +"Gaverete Ruskie?", +"Habla espanol?", +"Has the war started yet?", +"Have a nice T day.", +"Have a nice thanksgiving.", +"Have you been to Hawaii recently?", +"Have you been to Kauai since Iniki?", +"Have you ever been to New Madrid, Missouri?", +"Have you ever been to the Chatham islands?", +"Have you ever contacted Kalawao county, Molokai?", +"Have you ever contacted Loving county, Texas?", +"Have you ever played lemmings?", +"Have you ever played tetris?", +"Have you ever seen a computer virus?", +"How copy so far?", +"How copy?", +"How did you learn morse code?", +"How is my signal?", +"How is the weather?", +"How long have you been a radio amateur?", +"How old is your rig?", +"Hows my signal?", +"Hows the weather?", +"I am mobile and am going to the office.", +"I am mobile and am going to work.", +"I am mobile and driving to work.", +"I am mobile, driving cross country.", +"I am mobile, driving home from work.", +"I am mobile, riding a Peruvian Paso Fino horse.", +"I am mobile, riding a Peruvian Paso horse.", +"I am mobile, stuck in traffic.", +"I am using a linear amplifier.", +"I am using an iambic key.", +"I just saw a bolide.", +"I live at 10 Downing.", +"I live at 1010 Springwood.", +"I live at 1620 Keeaumoku.", +"I live at 2500 Pennsylvania.", +"I live at 61C Escondido.", +"I sometimes monitor ULF for earthquake precursors.", +"I sometimes monitor ulf for earthquake precursors.", +"I sometimes work packet on vhf.", +"Is it green there or brown?", +"Is the bad news true?", +"Is your state the one that doesnt have any city governments?", +"Is your state the one that has a unicameral legislature?", +"Is your state the one that has boroughs instead of counties?", +"Is your state the one that has parishes instead of counties?", +"Is your state the one that has townships instead of counties?", +"Ive fallen and I cant get up.", +"Jocks find quartz glyph, vex BMW.", +"Jocks find quartz glyph, vex BMW.", +"Jocks find quartz glyph, vex BMW.", +"Lots of QRM.", +"Mele kelikimaka and hauoli makahiki hou 1995.", +"Must QRT for Babylon V.", +"Must QRT for Star Trek.", +"Must QRT for bathroom break.", +"Must QRT for breakfast.", +"Must QRT for cold breakfast.", +"Must QRT for cold dinner.", +"Must QRT for cold supper.", +"Must QRT for diarreah break.", +"Must QRT for dinner.", +"Must QRT for hot breakfast.", +"Must QRT for hot dinner.", +"Must QRT for hot lunch.", +"Must QRT for hot supper.", +"Must QRT for lunch.", +"Must QRT for sleep.", +"Must QRT for some sleep.", +"Must QRT for supper.", +"Must QRT invading tanks making too much noise outside to hear you.", +"Must QRT occupation forces knocking at door.", +"Must QRT to hear news about approaching hurricane.", +"Must QRT wind has blown door open again.", +"Must QRT, lightning storm has started.", +"Must QRT, lightning threatens.", +"Must QRT, nuclear attack?", +"Must QRT, tornado sirens sounding.", +"Must QRT, tsunami sirens sounding.", +"Must QRT, weather radio warbling.", +"Our neighbors have a horse named morse code.", +"Our neighbors have a horse named morse.", +"Please QRS.", +"Propagation is good.", +"Propagation is poor.", +"Propagation is very good.", +"Propagation is very poor.", +"QLF?", +"QRG", +"QRH", +"QRI 1", +"QRI 2", +"QRI 3", +"QRI", +"QRJ", +"QRK 1", +"QRK 2", +"QRK 3", +"QRK 4", +"QRK 5", +"QRK", +"QRL", +"QRM", +"QRM?", +"QRN 1", +"QRN 2", +"QRN 3", +"QRN 4", +"QRN 5", +"QRN", +"QRO", +"QRP", +"QRP?", +"QRQ 10", +"QRQ 15", +"QRQ 20", +"QRQ 5", +"QRQ", +"QRS 10", +"QRS 15", +"QRS 20", +"QRS 5", +"QRS", +"QRS?", +"QRU", +"QRV", +"QRW", +"QRX 0300", +"QRX 0500", +"QRX 2000", +"QRX 2300", +"QRX", +"QRX?", +"QRY 1", +"QRY 2", +"QRY 3", +"QRY", +"QRZ", +"QSA 1", +"QSA 2", +"QSA 3", +"QSA 4", +"QSA 5", +"QSA", +"QSB", +"QSD", +"QSG 1", +"QSG 2", +"QSG", +"QSK", +"QSK?", +"QSL", +"QSM", +"QSN", +"QSO", +"QSP", +"QST", +"QSU", +"QSW", +"QSX", +"QSY", +"QSY?", +"QSZ", +"QTB", +"QTC", +"QTH?", +"QTR", +"QTR?", +"Should we QSY?", +"So how is the weather?", +"Sorry, stack of books just fell over.", +"Sprechen Sie Deutsch?", +"Thanks for the call.", +"There is a rainbow outside the window.", +"There is an eclipse of the moon right now.", +"Tnx for the call.", +"Tnx for the report.", +"Tnx for ur call.", +"We are on vacation and I am mobile.", +"We just felt a small quake here.", +"What are your hobbies?", +"What is the air pressure there?", +"What is the air quality there?", +"What is the air temperature there?", +"What is the humidity there?", +"What is your QTH?", +"What is your elevation?", +"What is your hobby?", +"What is your job?", +"What is your sign? I am a cancer.", +"What is your sign? I am a centaur.", +"What is your sign? I am a fish.", +"What time zone are you in?", +"Whats your job?", +"When did you start to learn morse code?", +"Where do you live now?", +"Where do you live?", +"Where is Hollister, California anyway?", +"Where is Parkfield, California anyway?", +"Whoa, we are having an earthquake here. A real roller.", +"Would you have given Morse a Nobel prize?", +"You are my first contact today.", +"You are my last contact today.", +"You are my second contact today.", +"You are using a nonlinear amplifier?", +"You are using an iambic key?", +"Your contest sequence no is 1035.", +"Your contest sequence no is 12.", +"Your contest sequence no is 17.", +"Your contest sequence no is 479.", +"Your contest sequence no is 73.", +"Your contest sequence no is 7v.", +"Your contest sequence no is 826.", +"Your contest sequence no is 99.", +"Your job is?", +"Your signal is chirpy.", +"am using a linear amplifier.", +"ewsqgbhjolpi oops cat on keyboard.", +"ru using linear amplifier?", diff --git a/qso/names.h b/qso/names.h new file mode 100644 index 0000000..125a1c3 --- /dev/null +++ b/qso/names.h @@ -0,0 +1,170 @@ +"Al", +"Alan", +"Alen", +"Alex", +"Alexander", +"Alexandra", +"Alexeev", +"Alice", +"Allen", +"Alma", +"Amber", +"Angel", +"Anne", +"Art", +"Barbara", +"Barbie", +"Bart", +"Bea", +"Betty", +"Bill", +"Bob", +"Bruce", +"Bryant", +"Bud", +"Carl", +"Carol", +"Cathy", +"Cheryl", +"Chris", +"Christy", +"Chuck", +"Dale", +"Dan", +"Dave", +"David", +"Dennis", +"Diane", +"Dick", +"Don", +"Ed", +"Edmund", +"Edward", +"Elaine", +"Eleuterio", +"Ellen", +"Francie", +"Frank", +"Fred", +"Gary", +"George", +"Gilda", +"Gus", +"Hadassah", +"Harry", +"Hedwig", +"Heika", +"Helen", +"Helmut", +"Henry", +"Heriberto", +"Ingrid", +"Isaac", +"Israel", +"Jack", +"Jacob", +"Jake", +"Jakob", +"James", +"Janae", +"Jane", +"Janet", +"Jeff", +"Jenni", +"Jessica", +"Jill", +"Jim", +"JimBob", +"Joan", +"Joe", +"John", +"Jon", +"Kaahumanu", +"Karen", +"Karl", +"Kathy", +"Keith", +"Ken", +"Kent", +"Kevin", +"Kristen", +"Kurt", +"Larry", +"Lauren", +"Liholiho", +"Linda", +"Lou", +"Lourdes", +"Lynda", +"Lynn", +"Marc", +"Margaret", +"Maria", +"Mariam", +"Mark", +"Marty", +"Marv", +"Mary", +"Mike", +"Monica", +"Moses", +"Mosha", +"Nancy", +"Nathan", +"Neil", +"Noelani", +"Oliver", +"Olivia", +"Pal", +"Pat", +"Patrick", +"Paul", +"Paula", +"Peter", +"Peto", +"Phil", +"Pieter", +"Quimby", +"Rachel", +"Ralph", +"Ray", +"Rebekkah", +"Reiko", +"Rex", +"Reza", +"Rich", +"Rick", +"Ron", +"Roy", +"Sally", +"Sam", +"Sara", +"Sarah", +"Scott", +"Scottie", +"Spencer", +"Steve", +"Stew", +"Stu", +"Sue", +"Tello", +"Terry", +"Thomas", +"Tim", +"Timothy", +"Todd", +"Tom", +"Tony", +"Umberto", +"Vicente", +"Victor", +"Wallace", +"Wally", +"Walt", +"Walter", +"Wendy", +"William", +"Xavier", +"Yaohui", +"Yolanda", +"Zelda", diff --git a/qso/newcity.h b/qso/newcity.h new file mode 100644 index 0000000..b2c6634 --- /dev/null +++ b/qso/newcity.h @@ -0,0 +1,29 @@ +"Albany", +"Avalon", +"Barnard", +"Bedford", +"Bethel", +"Boston", +"Brunswick", +"Canaan", +"Chester", +"Conway", +"Dime Box", +"Franklin", +"Granville", +"Hamilton", +"London", +"Morris", +"Oxford", +"Salisbury", +"Scarborough", +"Stafford", +"Stanton", +"Trenton", +"Troy", +"Walpole", +"Warren", +"Weston", +"Wilton", +"Windsor", +"Zulch", diff --git a/qso/put_form.c b/qso/put_form.c new file mode 100644 index 0000000..30faa4d --- /dev/null +++ b/qso/put_form.c @@ -0,0 +1,130 @@ +#include + +void +PutForm0 () +{ + PutRST (); + PutName (); + PutLocation (); + PutMisc (); + PutRig (); + PutWeather (); + PutJob (); + PutAge (); + PutMisc (); + PutQ_And_Freq (); + PutLicense (); +} + +void +PutForm1 () +{ + PutLocation (); + PutRST (); + PutRig (); + PutWeather (); + PutMisc (); + PutName (); + PutLicense (); + PutMisc (); + PutQ_And_Freq (); + PutAge (); + PutJob (); +} + +void +PutForm2 () +{ + PutThanks (); + PutRST (); + PutName (); + PutWeather (); + PutLocation (); + PutJob (); + PutLicense (); + PutRig (); + PutAge (); + PutQ_And_Freq (); +} + +void +PutForm3 () +{ + PutLocation (); + PutRST (); + PutRig (); + PutMisc (); + PutName (); + PutMisc (); + PutAge (); + PutJob (); + PutLicense (); + PutMisc (); + PutWeather (); + PutMisc (); + PutQ_And_Freq (); +} + +void +PutForm4 () + +{ + PutThanks (); + PutRST (); + PutJob (); + PutMisc (); + PutMisc (); + PutName (); + PutAge (); + PutLicense (); + PutRig (); + PutLocation (); + PutWeather (); + PutMisc (); +} + +void +PutForm5 () +{ + PutLocation (); + PutRST (); + PutRig (); + PutName (); + PutJob (); + PutAge (); + PutMisc (); + PutLicense (); + PutWeather (); + PutMisc (); + PutQ_And_Freq (); +} + +PutQSO () +{ + /* printf("VVV VVV\n") ; */ + PutFirstCallsign (); + switch (Roll (6)) + { + case 0: + PutForm0 (); + break; + case 1: + PutForm1 (); + break; + case 2: + PutForm2 (); + break; + case 3: + PutForm3 (); + break; + case 4: + PutForm4 (); + break; + default: + PutForm5 (); + break; + } + printf ("+ %%\n"); + PutLastCallsign (); + printf ("\n"); +} diff --git a/qso/rig.h b/qso/rig.h new file mode 100644 index 0000000..29646d6 --- /dev/null +++ b/qso/rig.h @@ -0,0 +1,69 @@ +"Collins 75S=3", +"Collins KWM=2", +"Drake T=4XB", +"Drake TR3", +"Drake TR4", +"Eico 753", +"Hallicrafters SR150", +"Hallicrafters SR160", +"Hallicrafters SR2000", +"Hallicrafters SR400", +"Hallicrafters SR500", +"Heathkit DX=660B", +"Heathkit HG=10B", +"Heathkit HR=10", +"Heathkit HW100", +"Heathkit HW12", +"Heathkit HW12A", +"Heathkit HW22", +"Heathkit HW22A", +"Heathkit HW32", +"Heathkit HW32A", +"Heathkit HW8", +"Heathkit SB100", +"Homebrew", +"Icom IC=1275", +"Icom IC=275", +"Icom IC=475", +"Icom IC=575", +"Icom IC=725", +"Icom IC=726", +"Icom IC=730", +"Icom IC=735", +"Icom IC=751", +"Icom IC=751A", +"Icom IC=765", +"Icom IC=781", +"Kenwood TR=450", +"Kenwood TR=751", +"Kenwood TR=850", +"Kenwood TR=851", +"Kenwood TS=140", +"Kenwood TS=440", +"Kenwood TS=680", +"Kenwood TS=711", +"Kenwood TS=77", +"Kenwood TS=850", +"National NCL 200", +"National NCX3", +"National NCX5", +"RS HTX=100", +"Radio Shack HTX=100", +"Swan 120", +"Swan 140", +"Swan 180", +"Swan 240", +"Swan 350", +"Ten Tec 562", +"Ten Tec 585", +"Ten Tec Argonaut", +"Ten Tec Delta", +"Ten Tec Omni", +"Yaesu FT=1000", +"Yaesu FT=101", +"Yaesu FT=736", +"Yaesu FT=747", +"Yaesu FT=757", +"Yaesu FT=767", +"Yaesu FT=840", +"Yaesu FT=990", diff --git a/qso/util.c b/qso/util.c new file mode 100644 index 0000000..5b70b62 --- /dev/null +++ b/qso/util.c @@ -0,0 +1,59 @@ +/* + ******************** + * Utility routines * + ******************** + */ +extern int their_age; + +int +CountStrings (StringVector) + char *StringVector[]; + /* + * Count the number of string values in the supplied vector + * of pointers. Start with the first pointer and stop when + * NIL (0) is encountered. + */ +{ +register char **SV; +register int Count; + + Count = 0; + + for (SV = StringVector; *SV; SV++) + { + Count++; + } + return (Count); +} + +int +Roll (Number) + int Number; +{ +int new_tmp; +double tmp_val; +double drand48 (); + tmp_val = ((int) (drand48 () * (Number /*-1*/ ))); + new_tmp = (int) tmp_val % 32767; + if (new_tmp < 2) + tmp_val = 2; + return ((int) new_tmp); +} + +int +License_Seed () +{ + if (their_age > 20) + return (20); + if (their_age < 10) + return (10); + return (their_age - 8); +} + +char * +Choose (Words, Number) + char *Words[]; + int Number; +{ + return (Words[Roll (Number)]); +} diff --git a/qso/weather.h b/qso/weather.h new file mode 100644 index 0000000..757e798 --- /dev/null +++ b/qso/weather.h @@ -0,0 +1,48 @@ +"a blue norther", +"a noreaster", +"a siberia express", +"chinook winds", +"cloudy", +"cold and windy", +"cold/dry", +"cool/windy", +"damp", +"drizzling", +"fog/drizzle", +"foggy", +"gale force winds", +"gentle rain", +"gusting trade winds", +"hailing", +"horizontal rain", +"hot and humid", +"hot and windy", +"hot", +"hot/dry", +"hot/humid", +"hot/muggy", +"hot/smoggy", +"humid", +"kona winds", +"pleasantly cool", +"pleasantly warm", +"raining cats and dogs", +"raining", +"santa anna winds", +"sleeting", +"smoggy", +"snowing", +"spitting rain", +"sunny", +"tornadic winds", +"trade winds", +"uncomfortably hot", +"very cold", +"very hot", +"very windy", +"warm", +"wet", +"wet/foggy", +"wet/windy", +"windy and warm", +"windy", diff --git a/test_input b/test_input new file mode 100644 index 0000000..61ad59e --- /dev/null +++ b/test_input @@ -0,0 +1,5 @@ +Jocks find quartz glyph, vex BMW. + +testing, testing, 1 2 3  different tone now.  Back again. + +VVV VVV