package Code.graphics.morseCode;
import java.awt.TextArea;
import java.awt.TextField;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import edu.cmu.ri.createlab.terk.robot.finch.Finch;
/**
* @author Erik Pasternak (epastern@andrew.cmu.edu)
*
* Created using work by Chris Bartley as a starting point. This module is
* designed to take input from the user either as dot '.', dash '-', space ' '
* input that it will translate into text or a text string that it will
* translate into Morse Code.
*
* To use this program, place the Finch's tail on a soft object so it is slightly inclined.
* Then tap on the tail to enter morse code (as a telegraph operator used to enter the code).
*/
public class MorseCodeBeeper
{
//MorseLookup is arranged with the alphabet, then letters
//e.g. 'a','b',...,'z','0','1','2',...,'9'
static final String[] MorseLookup = {".-", "-...", "-.-.", "-..", ".", "..-.",
"--.", "....", "..", ".---", "-.-", ".-..", "--", "-.", "---", ".--.", "--.-", ".-.",
"...", "-", "..-", "...-", ".--", "-..-", "-.--", "--..", "-----", ".----", "..---", "...--",
"....-", ".....", "-....", "--...", "---..", "----."};
//An array of strings is used because it makes lookup and appending to a string easier
static final String[] asciiLookup = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K",
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "0", "1", "2", "3", "4",
"5", "6", "7", "8", "9"};
final int ENCRYPT = 'e';
final int DECRYPT = 'd';
final int EXIT = 'x';
//whether or not we want to strictly enforce timing requirements for the morse interpreter
boolean STRICTMORSE = false;
TextArea messageOutput;
TextField morseOutput;
public MorseCodeBeeper() throws IOException
{
//First we initialize a new Finch, this finds a finch and connects to it.
//It currently hangs indefinitely if there is no Finch to connect to.
final Finch finch = new Finch();
//We'll also want some input from the keyboard, so start a reader for it
final BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
//This will store the message the user wants to change to morse
String Message;
//This stores morse the user wants to change to a message
String Morse;
//Whether or not an input is valid.
boolean isValid;
//Encrypt, Decrypt, or Exit option
int edxOpt;
//Min and Max accelerometer values
double[] accelVals;
//Vals for setting a threshhold and tracking the difference between min
//and max. Currently, only Thresh is used and it is set for halfway between
//the min and max.
double beepThresh, beepDiff;
System.out.println("Hello, Dave.");
System.out.println("Can you set up my accelerometer, please?");
//Gets the min and max vals for wherever you've set your Finch up
accelVals = MCB_setAccelerometer(in, finch);
//Halfway between is where we switch. Future versions need some hysteresis
beepThresh = (accelVals[0] + accelVals[1]) / 2;
beepDiff = accelVals[0] - accelVals[1];
if (beepDiff < 0)
{
beepDiff = -beepDiff;
}
System.out.println("Now then, what can I help you with today?");
//Repeat until we quit.
while (true)
{
//First we want to get whether we're encrypting, decrypting, or letting the user
//beep.
do
{
isValid = true;
System.out.println("Enter 'e' to encrypt, 'd' to decrypt, 'i' to input by" +
" bouncing the finch, or 'x' to exit:");
edxOpt = in.read();
if (edxOpt == EXIT)
{
//I can feel my mind going, Dave.
System.out.println("Daisy, Daisy, give me your answer truuuuuuuuuu...");
finch.quit();
System.exit(0);
}
//These are the only valid options, so clearly we need to take in new input.
else if (edxOpt != 'e' && edxOpt != 'd' && edxOpt != 'i')
{
System.out.println("I can't let you do that, Dave.");
isValid = false;
}
//Throw away any available bytes because you have to hit return.
while (in.ready())
{
in.read();
}
}
while (!isValid);
if (edxOpt == 'e')
{
//Red light means encrypt
finch.setLED(255, 0, 0);
//Get an input line and change it into morse. Stores as a string
//of dots and dashes.
Morse = MCB_GetMessage(in);
System.out.println("The encrypted message is");
System.out.println(Morse);
//Play it on the Finch's on board buzzer
MCB_PlayMorse(Morse, finch);
}
if (edxOpt == 'd')
{
//Blue light means decrypt
finch.setLED(0, 0, 255);
//Gets text input as .'s and -'s and translates it back into a message
Message = MCB_Decrypt(in);
System.out.println("The decrypted message is");
System.out.println(Message);
}
if (edxOpt == 'i')
{
System.out.println("Use the tail of the finch to input your message. Press any" +
" key to end.");
double pressed;
boolean wasPressed = false;
finch.setLED(0, 255, 0);
MorseCodeFrame frame = new MorseCodeFrame();
frame.setVisible(true);
while (true)
{
//Grab the current accelerometer values
pressed = finch.getXAcceleration();
if (pressed > beepThresh)
{
//Change our state to true if the tail is down
//the MorseCodeFrame.TAIL tells it the tail of the finch was
//the input so it knows to filter the result
frame.beepStateChanged(true, MorseCodeFrame.TAIL);
finch.buzz(frame.getFreq(), 50);
}
else
{
//otherwise change it to false
frame.beepStateChanged(false, MorseCodeFrame.TAIL);
}
//if the user hit enter quit
if (in.ready())
{
in.read();
break;
}
}
//delete the frame we made
frame.dispose();
System.out.println("All I can do is beep, Dave.");
}
System.out.println("");
}
}
/*This function is designed to take in a max and min input for the accelerometers
* on the Finch. This way the user can position it where they want and calibrate for
* however they found to balance the finch.
*/
double[] MCB_setAccelerometer(BufferedReader in, Finch finch) throws IOException
{
double[] minMax = {0, 0};
System.out.println("Place the tail of the Finch onto something you can press it" +
" down on and will bounch the tail back up when you release it.");
System.out.println("Let it sit unpressed and hit return.");
in.readLine();
minMax[1] = finch.getXAcceleration();
System.out.println("Now hold the tail of the finch down and hit return again.");
in.readLine();
minMax[0] = finch.getXAcceleration();
return minMax;
}
/*This function reads in a string from standard in and translates it into morse. The
* result is a string of dots '.' and dashes '-' with single spaces between characters
* and triple spaces between words for easy readability. Currently, only letters and
* the numbers 0-9 are accepted as inputs.
*/
String MCB_GetMessage(BufferedReader in) throws IOException
{
//Start with an empty string
String Morse = "";
int currChar;
int MorseIndex;
System.out.println("Enter the message to encrypt. Only letters, spaces, and" +
" numbers will be processed. All other characters will be" +
" ignored. Press 'Enter' when finished.");
do
{
//read the characters one at a time.
currChar = in.read();
//If it's lower case or uppercase subtract to have a/A reference index 0.
if (currChar >= 'a' && currChar <= 'z')
{
MorseIndex = currChar - 'a';
}
else if (currChar >= 'A' && currChar <= 'Z')
{
MorseIndex = currChar - 'A';
//Numbers start at 26 in the lookup table.
}
else if (currChar >= '0' && currChar <= '9')
{
MorseIndex = currChar - '0' + 26;
}
else if (currChar == ' ')
{
//If the input was a space append two spaces to our output. We end
//up with three total because each letter has a space after it already.
Morse = Morse + " ";
continue;
}
else
{
continue;
}
//This adds the string of dots and dashes that represent the letter, followed
//by a space.
Morse = Morse + MorseLookup[MorseIndex] + " ";
//We're done if we see a carriage return or a linefeed character.
}
while (currChar != 13 && currChar != 10);
//Return the generated string.
return Morse;
}
/*Reads in a string from standard in and translates it from .s and -s to text.
* Returns the translated string.
*/
String MCB_Decrypt(BufferedReader in) throws IOException
{
//Start with an empty string
String Message = "";
//The current input character
int currChar;
//Whether or not the last character seen was a space
boolean wasSpace = false;
//Whether or not the letter was recognized
boolean letterFound;
//Keep track of dots and dashes and only process them when we have a full letter
String currLetter = "";
System.out.println("Enter your message in morse code using '.' for dots and '-' for" +
" dashes. Put a single space between letters and a double space" +
" between words.");
do
{
//read the characters one at a time.
currChar = in.read();
//Add dots and dashes to the current letter.
if (currChar == '.')
{
currLetter += ".";
}
else if (currChar == '-')
{
currLetter += "-";
}
//On a space or a return we should finish the current letter and add it to
//the message.
if (currChar == ' ' || currChar == 13 || currChar == 10)
{
//If we have a letter, of course.
if (currLetter.length() > 0)
{
//In case we don't recognize it.
letterFound = false;
//Check through the lookup table for a match
for (int i = 0; i < MorseLookup.length; i++)
{
//If we find it add it to the message and break
if (currLetter.compareTo(MorseLookup[i]) == 0)
{
Message += asciiLookup[i];
letterFound = true;
break;
}
}
//if we didn't find it print an error
if (!letterFound)
{
System.out.printf("%s was not recognized and will be ignored\n", currLetter);
}
//and then clear our current letter track
currLetter = "";
}
//If this is the second space we've seen put a space into the message
if (wasSpace)
{
Message += " ";
//We set this to false because we want every two spaces to appear as one.
wasSpace = false;
}
//Otherwise this is a new space and set wasSpace to true
else
{
wasSpace = true;
}
}
//And if we saw anything else wasSpace becomes false.
else
{
wasSpace = false;
}
//We're done if it was a return
}
while (currChar != 13 && currChar != 10);
//Return the translated message
return Message;
}
/*This function will read a string of .'s, -'s and spaces and play it as morse
* code on the Finch's onboard buzzer. Two or more spaces will play as a space
* between words, while single spaces will play as a space between characters.
* Future versions should make the length of oneTick selectable.
*/
void MCB_PlayMorse(String Morse, Finch finch)
{
int oneTick = 150;
int frequency = 600;
//a dot is one tick
//a dash is three dots
//a space between dots and dashes is one dot
//a space between letters is 3 dots
//a space between words is 7 dots
for (int i = 0; i < Morse.length(); i++)
{
//For a single space delay three ticks, for multiple spaces delay 7 ticks
if (Morse.charAt(i) == ' ')
{
//We want to delay longer if there's more than one space.
if ((i + 1) < Morse.length() && Morse.charAt(i + 1) == ' ')
{
//Delay only 6 because there's a delay after every dot and dash already
MCB_DelayTicks(6);
//Keep incrimenting the counter until the next character isn't a space.
while (i + 1 < Morse.length() && Morse.charAt(i + 1) == ' ')
{
i++;
}
//again, only 2 delays here instead of 3.
}
else
{
MCB_DelayTicks(2);
}
}
//Plays a dot and then delays one tick. The buzzer on the finch runs in parallel
//with whatever we're doing, so we delay for two ticks. One for the buzz, one for
//the pause
else if (Morse.charAt(i) == '.')
{
finch.buzz(frequency, oneTick);
MCB_DelayTicks(2 * oneTick);
}
//Plays a dash and then delays for one tick
else if (Morse.charAt(i) == '-')
{
finch.buzz(frequency, oneTick * 3);
//three delays for the buzz and one for the space
MCB_DelayTicks(4 * oneTick);
}
}
}
//This function just sleeps the current thread for ticks number of miliseconds
void MCB_DelayTicks(int ticks)
{
try
{
Thread.sleep(ticks); // do nothing for ticks miliseconds
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
public static void main(final String[] args) throws IOException
{
new MorseCodeBeeper();
}
}