<html>
<xmp>
/**
 * Title: iSIM
 * Copyright: Copyright (c) 2002
 * Company:   University of Oregon Computer Science Dept.
 *
 * @author Jason Prideaux
 * @version 1.0
 *
 */

package gps_monitor.gps;

import gps_monitor.util.*;
import gps_monitor.gui.*;
import transport.protocol.*;
import transport.*;

import javax.comm.*;
import java.util.*;
import java.io.IOException;
import java.net.*;
import java.io.*;




public class NmeaSocketGps implements GpsGenerator, SocketReceiverListener{

	/**
	* Development version number
	*/
	public String VERSION_NUMBER = "1.0.4";

	/**
	* A string to describe one of the states a receiver could be in.
	*/
	public static final String ACTIVE = "active";

	/**
	* A string to describe one of the states a receiver could be in.
	*/
	public static final String INACTIVE = "inactive";

	/**
	* A string to describe one of the states a receiver could be in.
	*/
	public static final String OUT_OF_RANGE = "out_of_range";

	/**
	* A string to describe one of the states a receiver could be in.
	*/
	public static final String RECEPTION_WARNING = "reception_warning";


	private GpsMonitorUI ui;

	String currStatus = INACTIVE;
	double currHeading = -1.0;
	GlobalCoordinate position = new GlobalCoordinate(null,null,-9999999.9999999);
	double currSpeed = -1.0;
	long lastUpdate = -1;
	double presentAccuracy = -1.0;
	double worstCaseAccuracy = -1.0;
  	double longVar, latVar;
  	double totalLat = 0.0;
  	double totalLong = 0.0;
  	double longSquaredTotal = 0.0;
  	double latSquaredTotal = 0.0;
  	int accuracyUpdates = 0;
  	double hdop;
  	double pseudorangeError = -1.0;



	/* ====================================================================== */
  	/** This simple Constructor starts the transport connection to the iSIM
  	 *  simulator.
  	 *
  	 *  @param  ui  The parent to pass data up to.
  	 *  @param  port  The port to have transport package use.
  	 *
  	 */
  	public NmeaSocketGps (GpsMonitorUI ui, int port) throws PortInUseException, IOException, TooManyListenersException, UnsupportedCommOperationException{

   		SocketReceiver server = new SocketReceiver(this, port);
   		server.start();

  	} //Constructor



	/* ====================================================================== */
  	/** This method receives incoming messages from the transport class.  As
  	 *  required by being a SocketReceiverListener.
  	 *
  	 *  @param  msg  The incoming message.
  	 *
  	 */
	public void incomingMessage(Message msg){

		System.out.println("NmeaSocketGps got incoming message...");

		if( msg instanceof NMEAMessage ){

			setLatitude( new LatOrLongitude(""+((NMEAMessage)msg).getLatitude()) );
			setLongitude( new LatOrLongitude(""+((NMEAMessage)msg).getLongitude()) );
			setTrueHeading( ((NMEAMessage)msg).getHeading() );

		}


	} //method: incomingMessage






  /**
   * This method sets the status of the reciever
   *
   * @param status The new status of the reciever
   */
  public void setStatus(String status)
  {
  	if (!(status.equals(currStatus)))
	{
		currStatus = status;
	}
  }

  /**
   * This method gets the status of the reciever and implements the required
   * method in GpsGenerator interface
   *
   * @return the current status of the receiver
   */
  public String getStatus() throws NullPointerException
  {
  	if (currStatus != null)
    	return currStatus;
	else
		throw new NullPointerException("The status has not been set yet.");
  }

  /**
   * This method sets the latitude of the reciever.
   *
   * @param lat The new latitude of the object
   */
  public void setLatitude(LatOrLongitude lat)
  {
	if ((position.getLatitude() == null) || (!(position.getLatitude().equals(lat))))
	{
		position.setLatitude(lat);
	}
  }

  /**
   * This method gets the latitude of the reciever and implements the required
   * method in GpsGenerator interface
   *
   * @return the latitude of the receiver
   */
  public LatOrLongitude getLatitude() throws NullPointerException
  {
	if (position.getLatitude() != null)
		return position.getLatitude();
	else
		throw new NullPointerException("The latitude has not been set yet.");
  }

  /**
   * This method sets the longitude of the reciever.
   *
   * @param lon The new longitude of the object
   */
  public void setLongitude(LatOrLongitude lon)
  {
  	if ((position != null ) && (position.getLatitude() != null) && (position.getAltitude() != -9999999.9999999))
	{
		setUpPseudorangeError(position.getLatitude(), lon);
	}

	if ((position.getLongitude() == null) || (!(position.getLongitude().equals(lon))))
	{
		position.setLongitude(lon);
	}
  }

  /**
   * This method gets the longitude of the reciever and implements the required
   * method in GpsGenerator interface
   *
   * @return the longitude of the receiver
   */
  public LatOrLongitude getLongitude() throws NullPointerException
  {
	if (position.getLongitude() != null)
    	return position.getLongitude();
	else
		throw new NullPointerException("The longitude has not been set yet.");
  }

  /**
   * This method sets the altitude of the reciever
   *
   * @param altitude The altitude of the reciever
   */
  public void setAltitude(double altitude)
  {
  	if ((position.getAltitude() == -9999999.9999999) || (position.getAltitude() != altitude))
	{
		position.setAltitude(altitude);
	}
  }

  /**
   * This method gets the altitude of the reciever and implements the required
   * method in GpsGenerator interface
   *
   * @return The altitude of the reciever
   */
  public double getAltitude() throws NullPointerException
  {
  	if (position.getAltitude() != -9999999.9999999)
    	return position.getAltitude();
	else
		throw new NullPointerException("The altitude has not been set yet.");
  }

  /**
   * This method sets the heading which the reciever is on
   *
   * @param heading The heading of the reciever
   */
  public void setTrueHeading(double heading)
  {
  	if (currHeading != heading)
	{
		currHeading = heading;
	}
  }

  /**
   * This method returns the true heading of the receiver and implements the required
   * method in GpsGenerator interface
   *
   * @return The true heading of the receiver
   */
  public double getTrueHeading() throws NullPointerException
  {
  	if (currHeading != -1.0)
    	return currHeading;
	else
		throw new NullPointerException("The heading has not been set yet.");
  }

  /**
   * This method sets the time of the last update
   *
   * @param time The time at which the reciever last updated the location
   */
  public void setTimeLastLocationUpdate(int year, int month, int date, int hours, int mins, int secs)
  {
  	/*
	 * This is not the best way to specify the year.  Needs to be the
	 * number of years since 1900, and we only recieve a 2 digit year
	 * from the receiver.  It will work for one hundred years though!!
	 * Note the month's run from 0, ie January = 0.
	 */

	year += 2000;
	GregorianCalendar temp = new GregorianCalendar(year, (month - 1), date, (hours + 1), mins, secs);
  	long time = temp.getTime().getTime();
	//System.out.println(time);

  	if (lastUpdate != time)
	{
		lastUpdate = time;
	}
  }

  /**
   * This method gets the time of the last update and implements the required
   * method in GpsGenerator interface
   *
   * @return lastUpdate The time at which the reciever last updated the location
   */
  public long getTimeLastLocationUpdate() throws NullPointerException
  {
  	if (lastUpdate != -1)
    	return lastUpdate;
	else
		throw new NullPointerException("The last time update has not been set yet.");
  }

  /**
   * This method sets the speed of the reciever
   *
   * @param speed The present speed of the receiver
   */
  public void setSpeed(double speed)
  {
  	if (currSpeed != speed)
	{
		currSpeed = speed;
	}
  }

  /**
   * This method returns the current speed of the receiver and implements the required
   * method in GpsGenerator interface
   *
   * @return The current/present speed
   */
  public double getSpeed() throws NullPointerException
  {
  	if (currSpeed != -1.0)
    	return currSpeed;
	else
		throw new NullPointerException("The speed has not been set yet.");
  }

  /**
   * This method gets the present horizontal accuracy of the receiver
   *
   * @return The present accuracy of the receiver
   */
  public double getPresentHorizontalAccuracy()  throws NullPointerException
  {
  	if ((pseudorangeError != -1.0) && (hdop != -1.0))
	{
			presentAccuracy = hdop * pseudorangeError;
	}

  	if (presentAccuracy != -1.0)
    	return presentAccuracy;
	else
		throw new NullPointerException("The present accuracy has not been set yet.");
  }

  /**
   * This method gets the mean average horizontal accuracy of the receiver and implements the required
   * method in GpsGenerator interface
   *
   * @return The mean accuracy of the receiver
   */
  public double getMeanHorizontalAccuracy()  throws NullPointerException
  {
  	return -1.0;
  }

  /**
   * This method gets the worst case horizontal accuracy of the receiver and implements the required
   * method in GpsGenerator interface
   *
   * @return The worst case accuracy of the receiver
   */
  public double getWorstCaseHorizontalAccuracy() throws NullPointerException
  {
  	if (worstCaseAccuracy != -1.0)
    	return worstCaseAccuracy;
	else
		throw new NullPointerException("The worst-case accuracy has not been set yet.");
  }

  /**
   * This method sets the worst case horizontal accuracy of the receiver
   *
   * @param The worst case accuracy of the receiver
   */
  public void setHorizontalDOP(double dop)
  {
  	hdop = dop;
  }

  /**
   * This method allows us to obtain the 3D position of the gps receiver.
   * It returns this data as a GlobalCoordinate.
   *
   * @return The location of the gps receiver
   */
  public GlobalCoordinate getLocation()
  {
  	return position;
  }

  /**
   * This method helps in establishing a receivers accuracy.  Using this method
   * one can find the variance of horizontal coordinates and hence establish
   * a mean accuracy.
   *
   * @param lati A received latitude value
   * @param longi A received longitude value
   */
  public void setUpPseudorangeError(LatOrLongitude lati, LatOrLongitude longi)
  {
  	if (accuracyUpdates < 120)
	{
		accuracyUpdates++;
  		double[] latLong = LocationConverter.geospatialToCOORDS(lati, longi);
		double lat = latLong[1];
		double lon = latLong[1];

		totalLat += lat;
		totalLong += lon;
		latSquaredTotal += lat * lat;
		longSquaredTotal += lon* lon;

		if (accuracyUpdates == 120)
		{
			double latVariance = (latSquaredTotal - (totalLat * totalLat)/accuracyUpdates) / (accuracyUpdates - 1);
			double longVariance = (longSquaredTotal - (totalLong * totalLong)/accuracyUpdates) / (accuracyUpdates - 1);

			pseudorangeError = Math.sqrt(latVariance + longVariance);
			System.out.println("Pseudorange error set to: " + pseudorangeError);
		}
	}
  }

	public void setUI(GpsMonitorUI ui){
		this.ui = ui;
	}


} //class
</xmp>
</html>
