package net.ax86; import ewe.io.IOException; import org.json.JSONException; import ewe.io.InputStreamReader; import ewe.io.BufferedReader; import ewe.io.OutputStreamWriter; import ewe.io.BufferedWriter; import ewe.net.Socket; import org.json.JSONObject; /** * gpsd client library. * * This a simple library that allows low-level communication with the * GPS daemon. * * Things that are in C libgps but not in this library (or are done in a * different way): * - GPS::stream(): * - Works with JSON data only. That means there is no option to change * the reporting format (no WATCH_JSON, WATCH_NMEA, WATCH_RARE, * WATCH_RAW flags). * - Always uses the new protocol (no WATCH_NEWSTYLE, WATCH_OLDSTYLE * flags). * - Non-blocking mode is not yet implemented (no POLL_NONBLOCK flag). * I think this is the only real limitation of this implementation. * * Copyright (c) 2010 by Tilman Blumenbach. * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * @author Tilman Blumenbach * @version 0.9_ewe * * @see <a href="http://gpsd.berlios.de/gpsd.html">gpsd manpage for more information</a> */ public class GPS { /** * The major version of this API (NOT the gpsd API!). */ public static final int API_MAJOR = 0; /** * The minor version of this API (NOT the gpsd API!). */ public static final int API_MINOR = 9; /** * Disable watcher mode and other given WATCH_* flags. * * @see #stream( int ) */ public static final int WATCH_DISABLE = 1<<0; /** * Enable watcher mode and other given WATCH_* flags. * * @see #stream( int ) */ public static final int WATCH_ENABLE = 1<<1; /** * When reporting AIS data, scale integer quantities to floats if * they have a divisor or rendering formula assosiated with them. * * @see #stream( int ) */ public static final int WATCH_SCALED = 1<<2; /** * Restrict watching to a speciied device, patch given as second * argument to {@link #stream( int, String ) stream()}. * * @see #stream( int, String ) */ public static final int WATCH_DEVICE = 1<<3; /** * DEVICE flags: GPS data has been seen on this device. */ public static final int SEEN_GPS = 0x01; /** * DEVICE flags: RTCM2 data has been seen on this device. */ public static final int SEEN_RTCM2 = 0x02; /** * DEVICE flags: RTCM3 data has been seen on this device. */ public static final int SEEN_RTCM3 = 0x04; /** * DEVICE flags: AIS data has been seen on this device. */ public static final int SEEN_AIS = 0x08; /** * The Socket used for communication with gpsd. */ private Socket sock; /** * BufferedReader for the {@link #sock Socket}. * * @see #sock */ protected BufferedReader in; /** * BufferedWriter for the {@link #sock Socket}. * * @see #sock */ protected BufferedWriter out; /** * A {@link GPSHook} object used for certain callbacks. * * @see GPSHook * @see #setHook( GPSHook ) */ protected GPSHook hook = null; /** * Constructor: Creates a new GPS object and connects its * {@link #sock Socket} to a gpsd. * * @param host Host name or IP address to connect to. * @param port Port to connect to. * * @throws IOException E. g. on a connection failure. * * @see #cleanup() Call the <code>cleanup()</code> method when you * do not need this object anymore. */ public GPS( String host, int port ) throws IOException { this.sock = new Socket( host, port ); this.in = new BufferedReader( new InputStreamReader( this.sock.getInputStream() ) ); this.out = new BufferedWriter( new OutputStreamWriter( this.sock.getOutputStream() ) ); } /** * Constructor: Creates a new GPS object and connects its * {@link #sock Socket} to a gpsd. * * This simply calls {@link #constructor( String, int )} with the * default gpsd port 2947. * * @param host Host name or IP address to connect to. * * @throws IOException E. g. on a connection failure. * * @see #constructor( String, int ) * @see #cleanup() Call the <code>cleanup()</code> method when you * do not need this object anymore. */ public GPS( String host ) throws IOException { this( host, 2947 ); } /** * Sends data to gpsd. * * @param data The data to send. * * @throws IOException When data could not be written to the stream. */ public void send( String data ) throws IOException { this.out.write( data, 0, data.length() ); // Make sure it gets through: this.out.flush(); } /** * Asks gpsd for data from the last-seen fixes on all active GPS devices. * * This is a very simple method that just calls * {@link #send( String )} to send a "POLL" command to the * server. * * @throws IOException When data could not be written to the stream. * * @see #send( String ) */ public void poll() throws IOException { this.send( "?POLL;" ); } /** * Gets the next {@linkplain JSONObject JSON object} from gpsd. * * This call blocks when there is no data available. Use * {@link #waiting()} to check if there is data waiting. * * If there is a {@linkplain GPSHook hook} set, its * {@link GPSHook#onRawData( JSONObject, String )} method will be called. * * @return A {@link JSONObject} containing the data received from gpsd. * * @throws IOException On a stream reading failure. * @throws JSONException On a JSON parsing failure (e. g. syntax error). * * @see JSONObject * @see #waiting() * @see GPSHook#onRawData( JSONObject, String ) */ public JSONObject read() throws IOException, JSONException { String rawJSON = this.in.readLine(); JSONObject parsedJSON; if( rawJSON == null ) { throw new IOException( "End Of Stream reached" ); } parsedJSON = new JSONObject( rawJSON ); // Call our hook, if set: if( this.hook != null ) { this.hook.onRawData( parsedJSON, rawJSON ); } return parsedJSON; } /** * Checks whether there is data from gpsd to be {@linkplain #read() read}. * * Tblue> TODO: This does not seem to work correctly in Ewe (but it * does with Sun's JRE). Ewe bug? * * @return True if there is data to be read, false otherwise. */ public boolean waiting() { try { return this.in.ready(); } catch( IOException ignored ) { return false; } } /** * Enables/tunes gpsd watcher mode. * * @param flags Flags describing the action(s) to take. * @param arg A String treated as an argument to certain flags. * * @throws IOException E. g. on a {@link #sock Stream} write error. * @throws JSONException If we made a huge mistake like passing a * <code>null</code> String to one of the * {@link JSONObject#put()} methods. Should not * happen. * @throws GPSException If <code>arg</code> is <code>null</code> but * is needed by a flag. * * @see #WATCH_DISABLE * @see #WATCH_ENABLE * @see #WATCH_SCALED * @see #WATCH_DEVICE */ public void stream( int flags, String arg ) throws IOException, JSONException, GPSException { JSONObject GPSData = new JSONObject(); GPSData.put( "class", "WATCH" ); if( ( flags & WATCH_ENABLE ) != 0 ) { GPSData.put( "enable", true ); if( ( flags & WATCH_SCALED ) != 0 ) { GPSData.put( "scaled", true ); } if( ( flags & WATCH_DEVICE ) != 0 ) { if( arg == null ) { throw new GPSException( "No argument provided with WATCH_DEVICE" ); } GPSData.put( "device", arg ); } } else // WATCH_DISABLE? { GPSData.put( "enable", false ); if( ( flags & WATCH_SCALED ) != 0 ) { GPSData.put( "scaled", false ); } } // Send request to the server: this.send( "?WATCH=" + GPSData.toString() + ";" ); } /** * Enables/tunes gpsd watcher mode. * * This simply calls {@link #stream( int, String )} with an <code>arg</code> * parameter of <code>null</code>. * * @param int Flags describing the action(s) to take. * * @throws IOException E. g. on a {@link #sock Stream} write error. * @throws JSONException If we made a huge mistake like passing a * <code>null</code> String to one of the * {@link JSONObject#put()} methods. Should not * happen. * @throws GPSException If <code>arg</code> is <code>null</code> but * is needed by a flag. * * @see #stream( int, String ) stream( int, String ) for information * about flags. */ public void stream( int flags ) throws IOException, JSONException, GPSException { this.stream( flags, null ); } /** * Sets a {@linkplain GPSHook hook} to call on certain events. * * @param hook The {@link GPSHook} to set. * @see GPSHook */ public void setHook( GPSHook hook ) { this.hook = hook; } /** * "Destructor": Does the final clean up. * * Call this method when you do not this object anymore. */ public void cleanup() { // Close our socket. Also closes its Input and Output streams. this.sock.close(); } }