View Javadoc

1   /*
2    * Trip Tracker, a real-time position tracking system for the Internet.
3    * Copyright (C) 2006  Team Trip Tracker
4    *
5    * This program is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License as published by the
7    * Free Software Foundation; either version 2 of the License, or (at your
8    * option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful, but
11   * WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13   * General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License along
16   * with this program; if not, write to the Free Software Foundation, Inc.,
17   * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
18   */
19  
20  package triptracker.client.gps.core;
21  
22  import static gnu.io.SerialPortEvent.*;
23  import static gnu.io.SerialPort.*;
24  import static gnu.io.CommPortIdentifier.*;
25  import static triptracker.client.gps.core.GPSStatusState.*;
26  
27  import java.io.BufferedReader;
28  import java.io.FileNotFoundException;
29  import java.io.FileOutputStream;
30  import java.io.IOException;
31  import java.io.InputStreamReader;
32  import java.io.PrintWriter;
33  import java.util.ArrayList;
34  import java.util.Date;
35  import java.util.Enumeration;
36  import java.util.List;
37  import java.util.Set;
38  import java.util.TooManyListenersException;
39  import java.util.concurrent.CopyOnWriteArraySet;
40  import gnu.io.CommPortIdentifier;
41  import gnu.io.PortInUseException;
42  import gnu.io.SerialPort;
43  import gnu.io.SerialPortEvent;
44  import gnu.io.SerialPortEventListener;
45  import gnu.io.UnsupportedCommOperationException;
46  
47  import triptracker.core.Coordinate;
48  import triptracker.core.Utils;
49  
50  public class GPSConnection {
51  	/*** Listener set for Model View Controller (MVC) separation. */
52  	protected Set<GPSListener> listeners = new CopyOnWriteArraySet<GPSListener>();
53  
54  	private BufferedReader inputStream;
55  
56  	private SerialPort sPort;
57  
58  	private boolean closePort = false;
59  
60  	private boolean isConnected = false;
61  
62  	private boolean recieve = false;
63  
64  	private boolean validSignal = false;
65  
66  	private CommPortIdentifier portId;
67  
68  	PrintWriter outW;
69  
70  	private static final String WANTED_SENTENCE = "GPRMC";
71  
72  	private String portOwner = null;
73  
74  	public static final int BAUDRATE = 4800;
75  
76  	public static final int TIMEOUT = 2000;
77  
78  	private NMEASentence nmea = new NMEASentence();
79  
80  	private Coordinate currentCoordinate = null;// = new Coordinate(0,0, null);
81  
82  	/***
83  	 * Connects to the GPS-unit at the given port
84  	 * 
85  	 * @param port Port to connect
86  	 */
87  	public void open(String port) throws PortInUseException,
88  			TooManyListenersException, UnsupportedCommOperationException,
89  			IOException, IllegalArgumentException {
90  
91  		openFile();
92  		if (!isConnected()) {
93  			Enumeration portList = getPortIdentifiers();
94  
95  			if (!portList.hasMoreElements()) {
96  				System.out.println("No ports"); // XXX writing to console, test
97  			}
98  
99  			while (portList.hasMoreElements()) {
100 				portId = (CommPortIdentifier) portList.nextElement();
101 
102 				if (portId.getPortType() == PORT_SERIAL && portId.getName().equals(port)) {
103 					// Port found. Open it and setup port parameters.
104 					sPort = (SerialPort) portId.open("TripTracker", TIMEOUT);
105 					portOwner = portId.getCurrentOwner();
106 
107 					isConnected = true;
108 					statusUpdate(CONNECTED);
109 
110 					sPort.notifyOnDataAvailable(true);
111 					
112 					// XXX Added for debug testing.
113 					sPort.notifyOnBreakInterrupt(true);
114 					sPort.notifyOnCarrierDetect(true);
115 					sPort.notifyOnCTS(true);
116 //					sPort.notifyOnDataAvailable();
117 					sPort.notifyOnDSR(true);
118 					sPort.notifyOnFramingError(true);
119 					sPort.notifyOnOutputEmpty(true);
120 					sPort.notifyOnOverrunError(true);
121 					sPort.notifyOnParityError(true);
122 					sPort.notifyOnRingIndicator(true);
123 
124 					sPort.setSerialPortParams(BAUDRATE, DATABITS_8, STOPBITS_1,
125 							PARITY_NONE);
126 
127 					inputStream = new BufferedReader(new InputStreamReader(sPort
128 							.getInputStream()));
129 
130 					sPort.addEventListener(new DataListener());
131 
132 					return;
133 				}
134 
135 			}
136 		}
137 	}
138 
139 	/***
140 	 * Recieve data from GPS-unit
141 	 * @throws IOException 
142 	 */
143 	private void readGPSData() {
144 		String dataFromGPS = null;
145 		if (!recieve) {
146 			return;
147 		}
148 
149 		while (true) { // XXX Why loop infinitely.
150 			// Reads line for line from the GPS-unit
151 			try {
152 				dataFromGPS = inputStream.readLine();
153 			} catch (IOException e) {
154 				readExcepion(e);
155 				return;
156 			}
157 
158 			// Filters out other NMEA-sentences than then wanted
159 			if ((dataFromGPS.contains("$" + WANTED_SENTENCE))) {
160 				// Save Raw nmea to file
161 				outW.println(dataFromGPS);
162 				outW.flush();
163 				
164 				nmea.parseSentence(dataFromGPS);
165 				
166 				// Checks if the GPS is able to get a fixed position
167 				if (hasSignal()) {
168 					generateCoordinate();
169 					if (!validSignal) {
170 						validSignal = true;
171 						statusUpdate(HASFIXEDPOS);
172 					}
173 				} else if (validSignal) {
174 					validSignal = false;
175 					statusUpdate(HASFIXEDPOS);
176 				} else {
177 					statusUpdate(NOFIXEDPOS);
178 					System.out.println("no fixed pos");
179 				}
180 
181 			}
182 
183 			// Closing the port
184 			if (closePort) {
185 				statusUpdate(NOTCONNECTED);
186 				sPort.close();
187 			}
188 		}
189 	}
190 
191 	/***
192 	 * Generates a coordinate from the NMEA class
193 	 * 
194 	 */
195 	private void generateCoordinate() {
196 		// TODO Finish method, move this out from GPSConnection?
197 
198 		double lat = 0, lon = 0;
199 
200 		try {
201 			lat = Double.parseDouble(nmea.getField(3));
202 			lon = Double.parseDouble(nmea.getField(5));
203 		} catch (NumberFormatException e) {
204 			return;
205 		}
206 
207 		if (nmea.getField(4).contains("S")) {
208 			lat = -lat;
209 		}
210 		if (nmea.getField(6).contains("W")) {
211 			lon = -lon;
212 		}
213 
214 		Date date = Utils.parseDate(nmea.getField(9) + nmea.getField(1),
215 				"ddMMyyHHmmss");
216 
217 		if (date == null) {
218 			throw new NullPointerException("date could not be parsed: "
219 					+ nmea.getField(9) + nmea.getField(1));
220 		}
221 
222 		// Converts to decimal format
223 		lat = Coordinate.nmeaToDec(lat);
224 		lon = Coordinate.nmeaToDec(lon);
225 
226 		currentCoordinate = new Coordinate(lat, lon, date);
227 		//		System.out.println("Generated coord: " + currentCoordinate.toString());
228 	}
229 
230 	public Coordinate getCurrentCoordinate() {
231 		return currentCoordinate;
232 	}
233 
234 	/***
235 	 * Checks wheter the GPS has connection to the satellites
236 	 * 
237 	 * @return true if fixed position has been obtained, false if no fixed
238 	 * position is available
239 	 */
240 	public boolean hasSignal() {
241 		// if the time is specified, the gps-unit has enough data
242 		// to get a fixed possition?
243 		if (nmea.getField(1).length() == 6) {
244 			return true;
245 		} else {
246 			return false;
247 		}
248 	}
249 
250 	/***
251 	 * Returns a list of available serial ports.
252 	 * 
253 	 * @return list of available serial ports
254 	 */
255 	public List<String> getPorts() {
256 		List<String> ports = new ArrayList<String>();
257 		Enumeration portList = getPortIdentifiers();
258 
259 		while (portList.hasMoreElements()) {
260 			portId = (CommPortIdentifier) portList.nextElement();
261 
262 			if (portId.getPortType() == PORT_SERIAL) {
263 				ports.add(portId.getName());
264 			}
265 		}
266 
267 		if (ports.isEmpty()) {
268 			ports.add("NONE, PLEASE RESTART");
269 		}
270 		return ports;
271 	}
272 
273 	/***
274 	 * Checks if connected to the COM port
275 	 * 
276 	 * @return true if connected to the com port, else false
277 	 */
278 	public boolean isConnected() {
279 		return isConnected;
280 	}
281 
282 	/***
283 	 * Reports a broken listener, removes it from the listener queue and logs
284 	 * the error.
285 	 * 
286 	 * @param listener broken listener
287 	 * @param exception exception thrown by listener
288 	 */
289 	protected void brokenListener(GPSListener listener,
290 			RuntimeException exception) {
291 		System.err.println("Unexpected exception in listener");
292 		exception.printStackTrace(); // XXX Temp error logging. Add log to file?
293 
294 		// Remove listener from queue.
295 		listeners.remove(listener);
296 	}
297 
298 	/***
299 	 * Publish a connected event. 
300 	 * 
301 	 * @param connected true if connected, false if disconnected
302 	 */
303 	protected void connectedEvent(boolean connected) {
304 		for (GPSListener listener : listeners) {
305 			try {
306 				listener.connected(connected);
307 			} catch (RuntimeException e) {
308 				brokenListener(listener, e);
309 			}
310 		}
311 	}
312 
313 	/***
314 	 * Register a listener for server events.
315 	 * 
316 	 * @param listener event receiver to register
317 	 */
318 	public void addListener(GPSListener listener) {
319 		listeners.add(listener);
320 	}
321 
322 	/***
323 	 * Remove listener from listener queue.
324 	 * 
325 	 * @param listener event receiver to remove
326 	 */
327 	public void removeListener(GPSListener listener) {
328 		listeners.remove(listener);
329 	}
330 
331 	private class DataListener implements SerialPortEventListener {
332 		/***
333 		 * Handle serial port events
334 		 */
335 		public void serialEvent(SerialPortEvent event) {
336 			switch (event.getEventType()) {
337 			case BI:
338 				System.out.println("serialEvent: BI " + event.toString());
339 				break;
340 			case CD:
341 				System.out.println("serialEvent: CD " + event.toString());
342 				break;
343 			case CTS:
344 				System.out.println("serialEvent: CTS " + event.toString());
345 				break;
346 			case DATA_AVAILABLE: // The GPS-unit is transmitting data
347 				readGPSData();
348 				break;
349 			case DSR:
350 				System.out.println("serialEvent: DSR " + event.toString());
351 				break;
352 			case FE:
353 				System.out.println("serialEvent: FE " + event.toString());
354 				break;
355 			case OE:
356 				System.out.println("serialEvent: OE " + event.toString());
357 				break;
358 			case OUTPUT_BUFFER_EMPTY:
359 				System.out.println("serialEvent: OUTPUT_BUFFER_EMPTY " + event.toString());
360 				break;
361 			case PE:
362 				System.out.println("serialEvent: PE " + event.toString());
363 				break;
364 			case RI:
365 				System.out.println("serialEvent: RI " + event.toString());
366 				break;
367 			}
368 		}
369 	}
370 
371 	private void statusUpdate(GPSStatusState state) {
372 		for (GPSListener listener : listeners) {
373 			try {
374 				listener.statusUpdate(state);
375 			} catch (RuntimeException e) {
376 				brokenListener(listener, e);
377 			}
378 		}
379 	}
380 
381 	/***
382 	 * Closes the current COM port
383 	 *
384 	 */
385 	public void closePort() {
386 		if (sPort == null) {
387 			return;
388 		}
389 
390 		statusUpdate(NOTCONNECTED);
391 		System.out.println("CLOOS");
392 		sPort.close();
393 	}
394 
395 	/***
396 	 * 
397 	 * @return the ports owner
398 	 */
399 	public String getPortOwner() {
400 		if (portOwner == null) {
401 			portOwner = "Noone";
402 		}
403 		return portOwner;
404 	}
405 
406 	public boolean currCoordSet() {
407 		if (currentCoordinate == null) {
408 			return false;
409 		} else {
410 			return true;
411 		}
412 	}
413 
414 	/***
415 	 * Start recieve from the GPS-unit
416 	 *
417 	 */
418 	public void startRecieve() {
419 		System.out.println("recieve set to true");
420 		recieve = true;
421 	}
422 
423 	/***
424 	 * Stop/pause recieve from the GPS-unit
425 	 *
426 	 */
427 	public void stopRecieve() {
428 		System.out.println("recieve set to false");
429 		recieve = false;
430 	}
431 
432 	/***
433 	 * Checks if the signal is valid
434 	 * 
435 	 * @return true if valid signal
436 	 */
437 	public boolean isValidSignal() {
438 		return validSignal;
439 	}
440 
441 	/***
442 	 * Opens the given file
443 	 * 
444 	 * @throws FileNotFoundException 
445 	 */
446 	public void openFile() throws FileNotFoundException {
447 		outW = new PrintWriter(new FileOutputStream("nmeaout.txt"));
448 	}
449 
450 	/***
451 	 * Read exception has occured.
452 	 */
453 	protected void readExcepion(IOException ioe) {
454 		for (GPSListener listener : listeners) {
455 			try {
456 				listener.readExcepion(ioe);
457 			} catch (RuntimeException e) {
458 				brokenListener(listener, e);
459 			}
460 		}
461 
462 	}
463 }