Package jaron.autopilot

Source Code of jaron.autopilot.FlightGearGpsReceiver

package jaron.autopilot;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;

import javax.swing.event.EventListenerList;

import jaron.components.Signal;
import jaron.gps.Direction;
import jaron.gps.Latitude;
import jaron.gps.Longitude;
import jaron.gps.Trackpoint;
import jaron.gps.TrackpointListener;

/**
* The <code>FlightGearGpsReceiver</code> class provides a TCP/IP connection to the
* FlightGear flight simulator (FG) via the FG NMEA output interface.<br>
* <code>FlightGearGpsReceiver</code> runs as a thread and by instantiating it
* waits for the incoming connection from FG (at port {@value DEFAULT_PORT} by
* default).<br> The instantiating has to be done before FG starts up, or else
* FG reports a connection error and terminates.<br>
* Be aware that running FG and a Java application that uses the
* <code>FlightGearGpsReceiver</code> needs a computer with sufficient performance.
* The consequence of a lack of performance is, that some of the data sent by FG
* could be lost because of timing issues. In this case you could run FG and the
* Java application on two different machines via a network connection.<br>
*  
* @author      jarontec gmail com
* @version     1.2
* @since       1.2
*/
// TODO High-pass filter the calculated course signal (aileron jitter)
public class FlightGearGpsReceiver extends Thread {
  /**
   * The default update frequency of the <code>FlightGearGpsReceiver</code>
   * is set to {@value DEFAULT_UPDATE_FREQUENCY} Hz.
   */
  public static final float DEFAULT_UPDATE_FREQUENCY = 5;
 
  /**
   * The default port that is used for the incoming connection. At the moment
   * this is port {@value DEFAULT_PORT} but this could change in the future.
   */
  public static final int DEFAULT_PORT = 5557;
 
  private float updateFrequency = DEFAULT_UPDATE_FREQUENCY;
  private ServerSocket server = null;
  private Socket client = null;
  private EventListenerList trackpathListeners = new EventListenerList();
  private Signal latitude = new Signal();
  private Signal longitude = new Signal();
  private Signal courseOverGround = new Signal();
  private Signal speedOverGround = new Signal();
  private Signal altitudeAbsolute = new Signal();
  private Signal satellites = new Signal();
  private double pastLatitude = 0;
  private double pastLongitude = 0;

  /**
   * Creates a new <code>FlightGearGpsReceiver</code> and starts a <code>Thread</code>
   * that listens for incoming connections.
   *
   * @param port  the port the receiver listens to
   */
  public FlightGearGpsReceiver(int port) {
    // start the socket server who waits for the connecting client
    try {
      server = new ServerSocket(port); // from now on the server listens to incomming connections
    } catch (IOException e) {
      System.out.println("IOException in FlightGearGpsReceiver(): " + e.getMessage());
    }
    setDaemon(true);
    start();
  }

  /**
   * Creates a new <code>FlightGearGpsReceiver</code> and starts a <code>Thread</code>
   * that listens for incoming connections. It listens on the default port
   * <code>DEFAULT_PORT</code>.
   *
   * @see FlightGearGpsReceiver#DEFAULT_PORT
   */
  public FlightGearGpsReceiver() {
    this(DEFAULT_PORT);
  }
 
  /**
   * Adds a listener to the <code>EventListener</code> mechanism. So whenever
   * a trackpoint is added to the trackpath, the listener will be informed about
   * that event.
   *
   * @param listener  the listener to be added
   */
  public void addTrackpointListener(TrackpointListener listener) {
    // from now on the listener listens to trackpoint changes
    trackpathListeners.add(TrackpointListener.class, listener);
  }
 
  /**
   * Notifies all the listeners that added themselves to the <code>EventListener</code>
   * mechanism about the received NMEA data.
   *
   * @param trackpoint  the new trackpoint
   */
  protected synchronized void notifyTrackpointListeners(Trackpoint trackpoint) {
    for (TrackpointListener l : trackpathListeners.getListeners(TrackpointListener.class))
      l.trackpointChanged(trackpoint);
  }

  /**
   * Returns the latitude signal which is used for the event handling mechanism.
   *
   * @return the latitude signal
   *
   * @see Signal
   */
  public Signal getLatitude() {
    return latitude;
  }

  /**
   * Returns the longitude signal which is used for the event handling mechanism.
   *
   * @return the longitude signal
   *
   * @see Signal
   */
  public Signal getLongitude() {
    return longitude;
  }

  /**
   * Returns the course over ground signal which is used for the event handling
   * mechanism.
   *
   * @return the course over ground signal
   *
   * @see Signal
   */
  public Signal getCourseOverGround() {
    return courseOverGround;
  }

  /**
   * Returns the speed over ground signal which is used for the event handling
   * mechanism.
   *
   * @return the speed over ground signal
   *
   * @see Signal
   */
  public Signal getSpeedOverGround() {
    return speedOverGround;
  }

  /**
   * Returns the absolute altitude signal which is used for the event handling
   * mechanism.
   *
   * @return the absolute altitude signal
   *
   * @see Signal
   */
  public Signal getAltitudeAbsolute() {
    return altitudeAbsolute;
  }

  /**
   * Returns the satellite signal (number of satellites) which is used for the
   * event handling mechanism.
   *
   * @return the number of satellite signal
   *
   * @see Signal
   */
  public Signal getSatellites() {
    return satellites;
  }

  /**
   * Disconnects the connection to the client.
   */
  private void disconnectClient() {
    try {
      if (client != null) {
        client.close();
      }
    } catch (IOException e) {
      System.out.println("IOException in FlightGearGpsReceiver::disconnectClient(): " + e.getMessage());
    }
    finally {
      client = null;
    }
  }

  /**
   * Sets the update frequency for the <code>FlightGearGpsReceiver</code>.
   * The default update frequency is set to {@value DEFAULT_UPDATE_FREQUENCY} Hz.
   *
   * @param updateFrequency the new frequency in Hz
   */
  public void setUpdateFrequency(float updateFrequency) {
    this.updateFrequency = updateFrequency;
  }
 
  /* (non-Javadoc)
   * @see java.lang.Thread#run()
   */
  @Override
  public void run() {
    while(true) {
      try {
        // check if there is a client connected to our server
        if (client != null) {
          BufferedReader is = new BufferedReader(new InputStreamReader(client.getInputStream()));
          String s = new String();
          Trackpoint trackpoint = new Trackpoint();
          HashMap<String, String> nmea = new HashMap<String, String>();

          // FlightGear sends a stream of $GPRMC, $GPGGA and $GPGSA NMEA data (in that order)
          // this data is parsed and converted to a Trackpoint object
          // after that the Trackpoint object is sent to the EventListeners
          while ((s = is.readLine()) != null) {
            String[] params = s.split(",");
            if(params[0].equals("$GPRMC")) {
              nmea = new HashMap<String, String>();
              nmea.put("GPRMC", s);
              trackpoint.setLatitude(new Latitude(params[3], Direction.fromValue( params[4])));
              trackpoint.setLongitude(new Longitude(params[5], Direction.fromValue( params[6])));
              String time = params[1];
              String date = params[9];
              Calendar cal = GregorianCalendar.getInstance();
              cal.set(Calendar.HOUR, Integer.parseInt(time.substring(0, 2)));
              cal.set(Calendar.MINUTE, Integer.parseInt(time.substring(2, 4)));
              cal.set(Calendar.SECOND, Integer.parseInt(time.substring(4, 6)));
              cal.set(Calendar.DAY_OF_MONTH, Integer.parseInt(date.substring(0, 2)));
              cal.set(Calendar.MONTH, Integer.parseInt(date.substring(2, 4)));
              cal.set(Calendar.YEAR, Integer.parseInt(date.substring(4, 6)));
              trackpoint.setTimestamp(cal.getTime());
              trackpoint.setGroundSpeedKnots(Double.parseDouble(params[7]));
            }
            else if(params[0].equals("$GPGGA")) {
              nmea.put("GPGGA", s);
              trackpoint.setSatellites(Integer.parseInt(params[7]));
              trackpoint.setAltitude(Double.parseDouble(params[9]));
            }
            // $GPGSA implies the end of the NMEA data sequence
            else if(params[0].equals("$GPGSA")) {
              nmea.put("GPGSA", s);
              break;
            }
          }
          // FlightGear sends junk data at initialization and this is filtered out here
          if (trackpoint.getLatitude().getDegrees() != 0) {
            latitude.setValue(trackpoint.getLatitude().getDecimal());
            longitude.setValue(trackpoint.getLongitude().getDecimal());
            if (pastLatitude != 0 && pastLongitude != 0) {
              courseOverGround.setValue(Navigation.getCourseInDegrees(pastLatitude, pastLongitude, latitude.getValue(), longitude.getValue()));
            }
            speedOverGround.setValue(trackpoint.getGroundSpeed());
            altitudeAbsolute.setValue(trackpoint.getAltitude());
            satellites.setValue(trackpoint.getSatellites());
            notifyTrackpointListeners(trackpoint);
            pastLatitude = latitude.getValue();
            pastLongitude = longitude.getValue();
          }
        }
        // if no client is connected then check if there is a client waiting for a connection
        else if (server != null) {
          client = server.accept(); // waits until a connection is established
        }
      } catch (IOException e) {
          System.out.println("IOException in FlightGearNMEAReceiver::run(): IOException: " + e.getMessage());
          disconnectClient();
      }
      try { sleep((long )(1000 / updateFrequency)); } catch(InterruptedException e) {}
    }
  }

  /**
   * Disconnects the FlightGear client.
   */
  public void shutDown() {
    disconnectClient();
  }
}
TOP

Related Classes of jaron.autopilot.FlightGearGpsReceiver

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.