import java.io.*; import java.net.*; import java.util.HashMap; /** * This class provides location awareness to the granularity of a room * inside a building based on the reception of 802.11 broadcasts and the * corresponding detection of ultrasound. This class is intended to be used * by an application running on a mobile device. If the mobile device moves * close to a desktop running the ServerBeacon class/application, it will * know that it is in the same room as the desktop based on the information * provided by this MobileClient class. *
* An application using this class needs to provide a reference to a * LocationTracker object in the MobileClient class's constructor so that * the MobileClient class can signal the LocationTracker object when a room * change has occurred. *
* The MobileClient class implements both an UltrasoundUpdateListener * and a DatagramUpdateListener so that it can be informed of ultrasound * detections and datagram receptions. * * @author Tony Offer * @author Chris Palistrant * @version 1.0 */ public class MobileClient implements DatagramUpdateListener, UltrasoundUpdateListener, Constants { // Reference to the object/application that instantiated this // MobileClient protected LocationTracker notifyApp = null; // Reference to the UltrasoundListenr used throughout this class protected UltrasoundListener usl = null; // Note that no reference to a DatagramListener object needs to be // kept since this thread is just started once and there is no need // to access the object after that. The DatagramListener thread // will notify this MobileClient class when an 802.11 packet is // received. // This string identifies the current room in which this // MobileClient object perceives itself to exist. //protected String room = null; // History of the most recent ultrasound updates. This array // contains the room messages for the most recent ultrasound // detections in the order in which they were received. This // history is necessary to determine relative probabilities of being // in a given room. protected String [] detections = null; // The running index for the history of recent ultrasound updates. // This index constantly increments with wraparound so that the // oldest ultrasound updates are overwritten first. protected int latestUpdate = 0; // Whether or not to turn debugging information on. protected boolean debug = false; /** * This is the only constructor for this class. It takes a reference * to a LocationTracker object so that the MobileClient class * can notify the object when a room change has occurred. No other * constructor is provided because the MobileClient class must have * access to a roomChanged() method. * * @param extApp Reference to the LocationTracker object that * will receive room change notifications. */ public MobileClient(LocationTracker extApp, boolean onDebug) { notifyApp = extApp; debug = onDebug; // Start a datagram listener thread that will notify this class // of updates. Create a new UltrasoundListener that will be used // to record from the microphone and analyze the samples in the // hope of detecting ultrasound after an 802.11 datagram // reception. Both objects must be given a reference to this // MobileClient object since both threads have callback methods // to this MobileClient object. usl = new UltrasoundListener(this, debug); usl.start(); new DatagramListener(this).start(); // Initialize the array of recent ultrasound updates. Also // initialize the strings in this array since the history is // used almost immediately for analysis. detections = new String[HISTORY_SIZE]; for (int i = 0; i < detections.length; i++) detections[i] = ""; } /** * Callback method for the DatagramListener thread that receives * notifications of newly received datagram messages. This method * stores the received datagram message and the time at which it was * received for later correlation with ultrasound detections. If an * ultrasound detection is later correlated with the datagram * message received by this callback method, then the received * datagram message describes the current room location. * * @param timeDetected The time (in milliseconds since January 1, * 1970) at which the datagram message was * received. * @param message The string that was transmitted in the datagram. */ public void datagramDetected(String message, long timeDetected) { /*** TESTING ***/ System.out.println("\"" + message + "\" Received: " + //new java.util.Date(timeDetected)); timeDetected); // A datagram was detected so, the line should be analyzed for an // appropriate period of time, as long as the UltrasoundListener // is available to record. If the UltrasoundListener is not // available to record, the datagram message is ignored. if (usl.available()) { if (!usl.record(RECORD, message)) { System.out.println("Tried to record while already " + "recording. Exiting ..."); System.exit(1); } else System.out.println("Recording ..."); } else System.out.println("USListener was not available to record."); } /** * The UltrasoundListener thread uses this callback method to notify * the MobileClient object when ultrasound has been detected for the * room identified by the given string. This method is only called * when ultrasound has been successfully detected within the given * recording time period. Negative ultrasound detections must be * deduced by the lack of ultrasound detection for a given room * after a certain period of time (i.e. There has been no ultrasound * detection for the 802.11 room message "Room 232" after a period of * 5 seconds, so deduce that ultrasound was not detected for Room * 232). This callback method is used only for positive ultrasound * detections. * * @param roomMsg The string identifying the room for which * ultrasound has just been detected. */ public void ultrasoundDetected(String roomMsg) { System.out.println("US detected for " + roomMsg); // Place the room message in the most recent position of the // update history and increment the index. detections[latestUpdate] = roomMsg; latestUpdate = (latestUpdate+1) % HISTORY_SIZE; // To each of the room messages in the history, assign a relative // magnitude representing its presence in the history. More // recent room messages are given more weight. HashMap roomDistribution = new HashMap(); for (int weight = 1, i = latestUpdate; weight <= HISTORY_SIZE; weight++, i = (i+1) % HISTORY_SIZE) { // If room message already exists in the room distribution, // just add the magnitude to the existing magnitude. // Otherwise, just use the weight in the room distribution. int newMag = weight; if (roomDistribution.containsKey(detections[i])) { newMag = newMag + ((Integer) (roomDistribution.get(detections[i]))).intValue(); } // Put the magnitude at the key identified by the room // string unless the room string is blank, which happens at // the beginning of program execution when a history of // ultrasound detections has not yet been established. if (!detections[i].equals("")) roomDistribution.put(detections[i], new Integer(newMag)); } // Notify the LocationTracker of a newly calculated room // distribution. notifyApp.roomChanged(roomDistribution); // If the room for which ultrasound was detected is different // than the current room in which this MobileClient object // perceives itself to exist, notify the LocationTracker that a // room change has occurred. /* if (!roomMsg.equals(room)) { room = roomMsg; notifyApp.roomChanged(room); } */ } }