#!/usr/bin/perl # vim:ts=4 # Copyright (C) 2010 Paula Keezer. use strict; use Getopt::Long; use Term::ReadKey; use Acme::AGMorse qw(SetMorseVals SendMorseChr SendMorseMsg); ########################################## # Command line defaults ################# ########################################## my $DefaultInputFile = '<&STDIN'; my $DefaultOutputFile = '>&STDOUT'; my $Verbose = ""; my $Monitor = 0; ########################################## # Global strings (yuk) ################## ########################################## my $InputFile; my $OutputFile; my $Speed=20; # Size of speed to use; my $Weight=30; # Weight my $WordSource; # Words to be suffix transformed my $MaxMnt=30; # Maximum number of minutes to run my $Tone = 700; # Frequency in Hz of Monitor tone my $Duration = 5; # default duration is 5 minutes my $help; my $cl; # current line my $cf=__FILE__; # current file my $mySTDIN; ########################################## # function prototypes ################### ########################################## sub ExitNice; sub loadWordSource; #Words to be suffix transformed; sub transformWordHashKeys; #OwnerIncludesMinusExcludes; sub transformSelectWord; #make a string of a particular suffix sub openoutputCW; sub closeoutputCW; sub outputCW; #; sub inpWaiting; sub openSTDIN; sub closeSTDIN; ########################################## # body ################################# ########################################## ########################################## # Command argument parsing ############# ########################################## if (&GetOptions("v:s" => \$Verbose , m => \$Monitor , "i:s" => \$DefaultInputFile , "o:s" => \$DefaultOutputFile , "s:s" => \$Speed # speed 10 to 40 wpm , "w:s" => \$Weight # msec between retrys , "t:s" => \$Tone # Frequency in Hz for monitor tone , "r:s" => \$WordSource # Word Source file , "d:s" => \$Duration # Duration in minutes , "l:s" => \$MaxMnt # Maximum number of minutes run , h => \$help)) { if ($InputFile eq "") {$InputFile = $DefaultInputFile;} if ($OutputFile eq "") {$OutputFile = $DefaultOutputFile;} if ($Verbose =~ /0,/) { print STDERR ("arguments: \n", "Verbose = ", $Verbose,"\n", "Monitor = ", $Monitor,"\n", "InputFile = ", $InputFile,"\n", "OutputFile = ", $OutputFile,"\n", "Speede= ", $Speed,"\n", "Weight= ", $Weight,"\n", "Tone= ", $Tone,"\n", "WordSource= ", $WordSource, "\n", "MaxMnt= ", $MaxMnt, "Help = ", $help,"\n"); } if ($help) { ################################### # help text section ############### ################################### print STDERR <][-o ][-u ][-e ][-d ][-v virtmapping>[-h] Description This provides a perl program template for: Command line parameter - verbose messages, monitoring messages, input/output file specifications and help Program blocks for - command line parsing, help area text information, main processing loop and sub routines Options -v verbose messages to STDERR, following parameters are valid and can be strung together with ',' 0: 1: 2: 3: display word hash after loading word file 4: 5: 6: 7: dispaly word array after moving from hash 8: 9: display selected word after random selection example usage: $cf [other commands] -v 0,6 -i User specified input file default is (not used) -o User specified output file default is (not used) -s Speed default 20 wpm -w Weight of the dah element, default 30 (3 dit lengths) -t Tone default 700 Hz -r WordSource -d Duration in minutes to run Jeapordy -l MaxMnt -h This help text Files output file specified by user or STDOUT as default. Useful if you wat to store your word statistics in comma delimited format (import into excel for data analysis -e Word File: Each line is considered a unique word. You could load with most common words, or a large number of call signes or even complete sentences to build a unique Morse Code drill. Must be either in the directory that contains MorseJeapordy or a fully qualified path to the file EOF ExitNice; } } else { print STDERR ("usage: $cf [-v][-m][-i][-o][-u][-e][-r][-d][-h]\n"); } print STDERR "$OutputFile\n"; open (OUTPUTFILE, "$OutputFile") or die "Can't open $OutputFile"; ################################################################# # This is where things happen. Yes, very linear code that uses # many functions ################################################################# my %wordHash; # read in all the words from the file my @keyArray; # array to be used for random word sel print OUTPUTFILE "Start $cf\n"; loadWordSource ($WordSource,$Verbose); openSTDIN; openoutputCW($Speed,$Weight,$Tone); transformWordHashKeys ($Verbose); my $myWord = ""; my $endTime = time()/60 +$Duration; # Where the user interaction occurs print OUTPUTFILE "word, retrys, cnt, tot retrys, avg retry\n"; for (my $i = 0; $i<10000;$i++) { my $true = 1; my $cnt = 0; $myWord = transformSelectWord ($Verbose); while ($true) { outputCW ($myWord); if (!inpWaiting) { $true = 0; } $cnt++; } my $t1 = ++$wordHash{$myWord}[0]; my $t2 = $wordHash{$myWord}[1] += $cnt; printf (OUTPUTFILE "%s, %i, %i, %i, %.2f\n",$myWord, $cnt, $t1, $t2, $t2/$t1); if ($endTime < time()/60) { ExitNice; } } print OUTPUTFILE "End $cf\n"; ExitNice; ######################################### # not so normal clean up exit ########### ######################################### sub ExitNice { if ($Verbose =~ /0,/) { print STDERR "Finished processing \n";} if ($Monitor) { print STDERR "ExitNice\n";} if ($InputFile) { close (INPUTFILE); print STDERR "$_"; } closeoutputCW; closeSTDIN; exit (); } sub loadWordSource { my ($WordSource,$Verbose) = @_; my $True = 1; my $cl; my $CountMon = 0; my $In; # variable to contain input string use FileHandle; if ($WordSource eq "") { $cl = __LINE__; print STDERR ("$cf #$cl: ", 'Error, no WordSource file',"\n"); ExitNice; # temp end } open (INPUTFILE, "$WordSource") or die "Can't open $WordSource"; ######################################## # Start loop loading word hash loop ### ######################################## while ($True==1) { if ($In = ) { #Try to open InputFile chomp($In); # dump linefeed and spaces $In=~s/^\s+//; $In=~s/\s+$//; if ($Verbose =~ /0(,|$)/) { # if we're verbose, tell about us $cl = __LINE__; print STDERR ("$cf#$cl ", 'INPUTFILE Opened',"\n"); print STDERR ("INPUTFILE line \#$CountMon $In\n"); } if (exists $wordHash{$In}) {} else { $wordHash{$In}[0] = 0; $wordHash{$In}[1] = 0; } } else { if ($Verbose =~ /0(,|$)/) { $cl = __LINE__; print STDERR ("$cf #$cl", 'INPUTFILE not Opened',"\n"); ExitNice(); # No InputFile data. Then } $True = 0; } $CountMon++; } close (INPUTFILE); if ($Verbose =~ /3(,|$)/) { # Audit word hash content to STDERR print STDERR "Load WordSource hash Audit\n"; my $key; my $OutString; my $num; foreach $key (sort keys %wordHash) { $OutString = $key; $OutString = join ',',$key,$wordHash{$key}[0]; $OutString = join ',',$key,$wordHash{$key}[1]; print STDERR $OutString,"\n"; } print STDERR ("Leaving loadWordSource\n"); } } sub transformWordHashKeys { #OwnerIncludesMinusExcludes; my ($Verbose) = @_; my $key; my $i = 0; foreach $key (sort keys %wordHash) { $keyArray[$i] = $key; $i++; } ######################################################## # move keys, sorted to a list/array ####################################################### if ($Verbose =~ /7(,|$)/) { print STDERR "Transform key array audit\n"; for($i = 0; $i <= $#keyArray; $i++) { print STDERR "$keyArray[$i] \n"; } } } sub transformSelectWord { my ($Verbose) = @_; ################################################# # Select a random word #################################################a # set up random number # select word from keyArray my $wordRand = int rand($#keyArray); if ($Verbose =~ /9(,|$)/) { print STDERR " audit\n"; print STDERR "$keyArray[$wordRand] \n"; } return $keyArray[$wordRand]; } sub openoutputCW { my ($cspeed,$Weight, $Tone) = @_; SetMorseVals($cspeed,$Weight,$Tone); } sub closeoutputCW { } sub outputCW { my ($OutputWord) = @_; $OutputWord .= " "; SendMorseMsg($OutputWord); } sub openSTDIN { # open keyboard for non blocking keyboard check ReadMode 4; } sub closeSTDIN { # close keyboard for non blocking keyboard check # bad things happen if you don't do this ReadMode 0; } sub inpWaiting { # non blocking keyboard check if (not defined ($mySTDIN= ReadKey(-1))) {return 1;} else { if ($mySTDIN eq 'e') { closeSTDIN; ExitNice (); } return 0; } } =head1 NAME MorseJeapordy - listen to the Morse code and hit any key (except 'e') when you comprehend the word =head1 SCRIPT CATEGORIES Educational =head1 SYNOPSIS MorseJeapordy.pl [-v][-o ] [-s ] [-w ] [-d ] -e At the command line running: perl MorseJeapordy.pl -s 30 -w 30 -t 600 -d 5 -e 100_Common_Words.txt will test your ability to hear Morse Code at 30 wpm, a dah weight of 30 (or 3 dit lentghts) a tone of 600Hz, a duration of 5 minutes using a file in your current directory called 100_Common_Words.txt (available from programs like G4FOK Koch morse code) =head1 DESCRIPTION Pass the program the Morse Code Speed and Weight along with a file of Words. The program will start by sending 'paris' twice to calibrate your machine. Then the program will randomly select a word from the file you provided and send it through the local sound system. The program will repeat the word if you do not hit a key on the keyboard indicating that you recognized the word. (yes you can cheat, but what would be the point?) One you hit a key, the program will advance to the next random word =head1 OPTIONS -s Speed from 10 wpm to 50 wpm, default 20 wpm -w Weight of the 'dah' element from 20 to 60 where 30 is 3 dits long -t Tone of Morse Monitor, default 700 Hz -d Duration of exercised in minutes -e name of file that contains one word per line, any number of lines =head1 DIAGNOSTICS -v verbose messages to STDERR, following parameters are valid and can be strung together with ',' 0: 1: 2: 3: display word hash after loading word file 4: 5: 6: 7: dispaly word array after moving from hash 8: 9: display selected word after random selection =head1 REQUIRES Acme::AGMorse A Morse Code Module for perl Audio::Beep A beep module with freq and duration callable from perl You must install Audio::Beep and test with the Beep command on the command line before installing Acme::AGMorse. You must install Acme::AGMorse before running MorseJeapordy the -e option and a word file. Each line is considered a unique word. You could load with most common words, or a large number of call signes or even complete sentences to build a unique Morse Code drill. Must be either in the directory that contains MorseJeapordy or a fully qualified path to the file =head1 README This is a Linux Morse Code game that produces Morse Code sound of random words. The user is suppose to hit any key on the keyboard when she/he recognizes the woword and the the program will score and move on to the next word. The command line provides options for speed, dah weight, tone, length of game, and custom word file. You must have Audio::Beep installed and running to hear anything You then must install Acme::AGMorse, it's make test will send a message at 20 wpm. Once you can hear the test message, you can run MorseJeapordy with a word file of your choice (G4FOK has a very nice set of files in his Koch Morse training program) More details can be found by running MorseJeapordy.pl -h =head1 AUTHOR Paula Keezer/NX1P nx1p @at@ arrl.net =cut