Statistics
| Revision:

root / trunk / extensions / extGPS / src / org / gvsig / gps / GPSDriver.java @ 4914

History | View | Annotate | Download (16.3 KB)

1
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
2
 *
3
 * Copyright (C) 2005 IVER T.I. and Generalitat Valenciana.
4
 *
5
 * This program is free software; you can redistribute it and/or
6
 * modify it under the terms of the GNU General Public License
7
 * as published by the Free Software Foundation; either version 2
8
 * of the License, or (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, write to the Free Software
17
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
18
 *
19
 * For more information, contact:
20
 *
21
 *  Generalitat Valenciana
22
 *   Conselleria d'Infraestructures i Transport
23
 *   Av. Blasco Ib??ez, 50
24
 *   46010 VALENCIA
25
 *   SPAIN
26
 *
27
 *      +34 963862235
28
 *   gvsig@gva.es
29
 *      www.gvsig.gva.es
30
 *
31
 *    or
32
 *
33
 *   IVER T.I. S.A
34
 *   Salamanca 50
35
 *   46005 Valencia
36
 *   Spain
37
 *
38
 *   +34 963163400
39
 *   dac@iver.es
40
 */
41

    
42
/* CVS MESSAGES:
43
*
44
* $Id: GPSDriver.java 4914 2006-04-20 17:13:35Z jaume $
45
* $Log$
46
* Revision 1.12  2006-04-20 17:13:35  jaume
47
* *** empty log message ***
48
*
49
* Revision 1.11  2006/04/12 10:03:32  jaume
50
* *** empty log message ***
51
*
52
* Revision 1.10  2006/04/11 20:01:18  jaume
53
* *** empty log message ***
54
*
55
* Revision 1.9  2006/04/11 13:25:54  jaume
56
* *** empty log message ***
57
*
58
* Revision 1.8  2006/04/11 13:19:51  jaume
59
* *** empty log message ***
60
*
61
* Revision 1.7  2006/04/10 11:21:52  jaume
62
* *** empty log message ***
63
*
64
* Revision 1.6  2006/04/07 12:45:55  jaume
65
* *** empty log message ***
66
*
67
* Revision 1.5  2006/04/07 11:10:26  jaume
68
* *** empty log message ***
69
*
70
* Revision 1.4  2006/04/07 08:27:48  jaume
71
* *** empty log message ***
72
*
73
* Revision 1.3  2006/04/06 10:34:46  jaume
74
* *** empty log message ***
75
*
76
* Revision 1.1  2006/04/05 17:08:18  jaume
77
* *** empty log message ***
78
*
79
* Revision 1.2  2006/04/03 21:07:35  jaume
80
* *** empty log message ***
81
*
82
* Revision 1.1  2006/04/03 16:10:27  jaume
83
* *** empty log message ***
84
*
85
* Revision 1.1  2006/03/31 09:55:34  jaume
86
* *** empty log message ***
87
*
88
*
89
*/
90
package org.gvsig.gps;
91

    
92
import gnu.io.CommPortIdentifier;
93
import gnu.io.PortInUseException;
94
import gnu.io.SerialPort;
95
import gnu.io.UnsupportedCommOperationException;
96

    
97
import java.awt.geom.Point2D;
98
import java.io.BufferedWriter;
99
import java.io.File;
100
import java.io.FileWriter;
101
import java.io.IOException;
102
import java.io.InputStream;
103
import java.io.OutputStream;
104
import java.util.ArrayList;
105
import java.util.Enumeration;
106
import java.util.Hashtable;
107
import java.util.Iterator;
108

    
109
import org.gvsig.gps.exceptions.GPSReceiverException;
110
import org.gvsig.gps.listeners.GPSEventListener;
111
import org.gvsig.gps.parser.NMEA.GGASentence;
112
import org.gvsig.gps.parser.NMEA.GSASentence;
113
import org.gvsig.gps.parser.NMEA.IllegalSentenceException;
114
import org.gvsig.gps.parser.NMEA.NMEASentence;
115
import org.gvsig.gps.parser.NMEA.NMEASentenceFactory;
116
import org.gvsig.gps.parser.NMEA.RMCSentence;
117
import org.gvsig.gps.parser.NMEA.VTGSentence;
118

    
119
import com.iver.andami.PluginServices;
120

    
121
/**
122
 * <p>
123
 * Singleton class that handles the communication within the application and the
124
 * GPS receiver. It opens the port set with the setPort() method and establishes
125
 * the comunication via NMEA protocol.<br>
126
 * </p>
127
 
128
 * @author jaume dominguez faus - jaume.dominguez@iver.es
129
 *
130
 */
131
public class GPSDriver extends Thread {
132
        private boolean connected = false;
133
        private ArrayList eventListeners = new ArrayList();
134
        
135
        // The timeOut field specifies how long a value received from the device
136
        // is valid. After this time, no more events of the corresponding class
137
        // will be fired.
138
        private static final long timeOut = 60 * 1000; // 60 seconds.
139
        
140
        private SerialPort serialPort = null;
141
        private InputStream inputStream;
142
        private OutputStream outputStream;
143

    
144
        private Hashtable register        = new Hashtable();
145
        private int rate = 1000;
146
        private long lastSampleTime;
147
        private boolean eventsEnabled;
148

    
149
        private double lonOffset = 0D;
150
        private double latOffset = 0D;
151
        private Point2D currentPos;
152
        
153
        private static GPSDriver instance = null;
154
        private static String fileName = "c:/gps points.txt";
155
        static BufferedWriter bw ;
156
        
157
        /**
158
         * Public instantiation of the driver is forbidden.
159
         */
160
        private GPSDriver() {};
161
        
162
        /**
163
         * <p>
164
         * This is a singleton object. Use this method to get the only one instance allowed.<br>
165
         * </p>
166
         * <p>
167
         * The use of the GPSDriver is very simple. Just set the port and the communciation
168
         * attributes through the setPort(...) method. Register a new listener into the driver
169
         * using the addEventListener(GPSEventListener) method. Then call start, stop as far
170
         * as it is a thread and your listeners will be notified about any event received
171
         * from the device.
172
         * </p>
173
         * @return
174
         */
175
        public static GPSDriver getInstance() {
176
                if (instance == null) 
177
                        instance = new GPSDriver();
178
                if (bw == null)
179
                        try {
180
                                bw = new BufferedWriter(new FileWriter(new File(fileName)));
181
                        } catch (IOException e) {
182
                                e.printStackTrace();
183
                        }
184
                return instance;
185
        }
186
        
187
        /**
188
         * Sets the port and its attributes used to comunicate to the receiver.
189
         * @param CommPortIdentifier portID, the port identifier
190
         * @param int portSpeed, the port speed expressed in bauds
191
         * @param int dataBits, value for the communication's data bits
192
         *                 (one of SerialPort.DATABITS_5, SerialPort.DATABITS_6, 
193
         *                                 SerialPort.DATABITS_7, or SerialPort.DATABITS_8)
194
         * @param int stopBits, value for the communication's stop bits
195
         *                 (one of SerialPort.STOPBITS_1, SerialPort.STOPBITS_1_5, or SerialPort.STOPBITS_2)
196
         * @param int parity, value for the communication's parity 
197
         *                 (one of SerialPort.PARITY_EVEN, SerialPort.PARITY_MARK,
198
         *                                 SerialPort.PARITY_NONE, SerialPort.PARITY_ODD, or
199
         *                          SerialPort.PARITY_SPACE). 
200
         * @throws PortInUseException
201
         */
202
        public void setPort(CommPortIdentifier portID, int portSpeed, int dataBits, int stopBits, int parity) throws PortInUseException{
203
                close();
204
                getInstance().serialPort = (SerialPort) portID.open("gvSIG", portSpeed);
205
                try {
206
                        getInstance().inputStream = getInstance().serialPort.getInputStream();
207
                        getInstance().outputStream = getInstance().serialPort.getOutputStream();
208
                        getInstance().serialPort.setSerialPortParams(
209
                                                                portSpeed, 
210
                                                                dataBits,
211
                                                                stopBits,
212
                                                                parity
213
                                                );
214
                } catch (UnsupportedCommOperationException e) {
215
                        e.printStackTrace();
216
                } catch (IOException e) {
217
                        e.printStackTrace();
218
                }
219
        }
220
        
221
        /**
222
         * Tells the driver to start monitoring and capturing data from the device.
223
         */
224
        public void connect() {
225
                getInstance().eventsEnabled = true;
226
                
227
                if (!getInstance().isAlive()) {
228
                        System.out.println("start");
229
                        getInstance().start();        
230
                }
231
        }
232
        
233
        public void run() {
234
                byte[] readBuffer = new byte[2048];
235
                StringBuffer line = new StringBuffer();
236
                try {
237
                        for (int bytes = getInstance().inputStream.read(readBuffer); bytes>-1; bytes = getInstance().inputStream.read(readBuffer)){
238
                                
239
                                // Notify the listeners that the GPS is connected.
240
                                Iterator it = eventListeners.iterator();
241
                                while (!getInstance().connected && getInstance().eventsEnabled && it.hasNext()) {
242
                                        GPSEventListener l = (GPSEventListener) it.next();
243
                                        l.connectionEstablished();
244
                                        
245
                                }
246
                                getInstance().connected = true;
247
                                
248
                                // Creates a new buffer to contain the previous readed bytes and the next bunch of bytes
249
                                String str = new String(readBuffer).substring(0, bytes);
250
                                int i = str.indexOf("\n");
251
                                if (i == -1) {
252
                                        line.append(str);
253
                                } else {
254
                                        line.append(str.substring(0,i));
255
                                        analyzeMessage(line.toString());
256
                                        
257
                                        line = new StringBuffer();
258
                                        line.append(str.substring(i+1,str.length()));
259
                                }
260
                        }
261
                        System.err.println("Sending request");
262
                        getInstance().outputStream.write(new String("$PMCAG,005,1,GGA,001").getBytes());
263
                        for (int bytes = getInstance().inputStream.read(readBuffer); bytes>-1; bytes = getInstance().inputStream.read(readBuffer)){
264
                                // Creates a new buffer to contain the previous readed bytes and the next bunch of bytes
265
                                String str = new String(readBuffer).substring(0, bytes);
266
                                int i = str.indexOf("\n");
267
                                if (i == -1) {
268
                                        line.append(str);
269
                                } else {
270
                                        line.append(str.substring(0,i));
271
                                        analyzeMessage(line.toString());
272
                                        
273
                                        line = new StringBuffer();
274
                                        line.append(str.substring(i+1,str.length()));
275
                                }
276
                                break;
277
                        }
278
                getInstance().inputStream = null;
279
                getInstance().outputStream = null;
280
                } catch (IOException e) {
281
                        e.printStackTrace();
282
                } catch (NullPointerException e) {
283
                }
284
                
285
                // Connection lost, will notify the listeners
286
                getInstance().connected = false;
287
                Iterator it = getInstance().eventListeners.iterator();
288
                while (getInstance().eventsEnabled && it.hasNext()) {
289
                        GPSEventListener l = (GPSEventListener) it.next();
290
                        l.connectionLost();
291
                }
292
                
293
        }
294
        
295
        /**
296
         * Parses the specific NMEA message.
297
         * @param line
298
         */
299
        private void analyzeMessage(String line) {
300
                try {
301
                        System.out.println("Analizing: "+line);
302
                        NMEASentence data = NMEASentenceFactory.createFromString(line);
303
                        NMEASentence oldData = (NMEASentence) getInstance().register.get(data.getName());
304
                        boolean mustNotifyListeners = !data.isEquivalentTo(oldData);
305
                        getInstance().register.put(data.getName(), data);
306
                        //if (mustNotifyListeners)
307
                                fireEvents();
308
                } catch (IllegalSentenceException e) {
309
                }
310
                
311
        }
312
        
313
        public static void sleep(long millis) {
314
                getInstance().sleep(millis);
315
        }
316
        
317
        public static void sleep(long millis, long nanos) {
318
                getInstance().sleep(millis, nanos);
319
        }
320
        
321
        /**
322
         * Sets the minimum interval between events in milliseconds.
323
         * @param millis
324
         */
325
        public void setSampleRate(int millis) {
326
                getInstance().rate = millis;
327
        }
328
        
329
        /**
330
         * Registers a new listener that will be notified about any event occured from
331
         * the gps receiver.
332
         * @param GPSEventListener l
333
         */
334
        public void addEventListener(GPSEventListener l) {
335
                getInstance().eventListeners.add(l);
336
        }
337
        
338
        /**
339
         * Iterates over the message registry and notifies the listeners
340
         */
341
        private void fireEvents() {
342
                Iterator it = getInstance().eventListeners.iterator();
343
                while (getInstance().eventsEnabled && it.hasNext()) {
344
                        GPSEventListener l = (GPSEventListener) it.next();
345
                        if (System.currentTimeMillis() - getInstance().lastSampleTime  >= getInstance().rate ) {
346
                                Iterator i = getInstance().register.keySet().iterator();
347
                                while (i.hasNext()) {
348
                                        NMEASentence aux = (NMEASentence) getInstance().register.get(i.next());
349
                                        if (isCurrent(aux)) {
350
                                                // This is a valid record
351
                                                if (aux instanceof GGASentence) {
352
                                                        GGASentence s = (GGASentence) aux;
353
                                                        {
354
                                                                try {
355
                                                                        bw.write(s.getLongitude() + "," + s.getLatitude()+"\n");
356
                                                                        bw.flush();
357
                                                                } catch (IOException e) {
358
                                                                        // TODO Auto-generated catch block
359
                                                                        e.printStackTrace();
360
                                                                }
361
                                                        }
362
                                                        getInstance().fireLonLatPositionReceived(l, s.getLongitude(), s.getLatitude());
363
                                                        
364
                                                        l.precisionChanged(-1, s.getHDOP(), -1);
365
                                                        l.signalQualityChanged(0, s.getSatelliteCount(), s.getQualityStatus());
366
                                                        l.altitudeChanged(s.getAltitude());
367
                                                } else if (aux instanceof GSASentence) {
368
                                                        GSASentence s = (GSASentence) aux;
369
                                                        float[] dissolutions = s.getPrecisionDisolutions();
370
                                                        l.precisionChanged(dissolutions[0], dissolutions[1], dissolutions[2]);
371
                                                        s.getUsedSatellites();
372
                                                } else if (aux instanceof RMCSentence) {
373
                                                        l.unhandledMessage(aux.toString());
374
                                                } else if (aux instanceof VTGSentence) {
375
                                                        VTGSentence s = (VTGSentence) aux;
376
                                                        l.speedChanged(s.getSpeed(), s.getCourse());
377
                                                } else {
378
                                                        l.unhandledMessage(aux.toString());
379
                                                }
380
                                        } else {
381
                                                // This record is too old.
382
                                                getInstance().register.remove(aux);
383
                                        }
384
                                }
385

    
386
                                getInstance().lastSampleTime = System.currentTimeMillis();
387
                        }
388
                }
389
        }
390
        
391
        /**
392
         * Saves the position that is going to be delivered and notifies to the 
393
         * listener passed in the first agument that a new position has been received.
394
         * This is a convenience method and should not be called outside the fireEvents
395
         * method.
396
         * @param listener
397
         * @param longitude
398
         * @param latitude
399
         */
400
        private void fireLonLatPositionReceived(GPSEventListener listener, double longitude, double latitude) {
401
                getInstance().currentPos = new Point2D.Double(longitude, latitude);
402
                listener.newLonLatPositionReceived(longitude - lonOffset, latitude - latOffset);
403
        }
404

    
405
        /**
406
         * Tells if the sentence is considered as up-to-date.
407
         * @param NMEASentence
408
         * @return boolean
409
         */
410
        private boolean isCurrent(NMEASentence n) {
411
                return System.currentTimeMillis() - n.getTime() < timeOut;
412
        }
413
        
414
        /**
415
         * Silences the event firing. To resume the event firing just call <b>start()</b> method.  
416
         */
417
        public void silence() {
418
                getInstance().eventsEnabled = false;
419
        }
420
        
421
        /**
422
         * Closes the connection and frees any resource that the driver could be using.
423
         */
424
        public void close() {
425
                stop();
426
                if (getInstance().serialPort != null) {
427
                        getInstance().serialPort.notifyOnDataAvailable(false);
428
                        getInstance().serialPort.removeEventListener();
429
                        if (getInstance().inputStream != null) {
430
                                try {
431
                                        getInstance().inputStream.close();
432
                                }
433
                                catch (IOException e) {}
434
                        }
435
                        if (getInstance().outputStream != null) {
436
                                try {
437
                                        getInstance().outputStream.close();
438
                                        getInstance().outputStream = null;
439
                                }
440
                                catch (IOException e) {}
441
                        }
442
                        getInstance().serialPort.close();
443
                        getInstance().serialPort = null;
444
                }
445
                getInstance().connected = false;
446
                Iterator it = getInstance().eventListeners.iterator();
447
                while (getInstance().eventsEnabled && it.hasNext()) {
448
                        GPSEventListener l = (GPSEventListener) it.next();
449
                        l.connectionLost();
450
                }
451
                instance = null;
452
                
453
        }
454
        
455
        /**
456
         * Sets the offset of the GPS receiver. By default it is 0, but sometimes it
457
         * is useful to calibrate it.
458
         * 
459
         * @param lonOffset
460
         * @param latOffset
461
         * @throws GPSReceiverException 
462
         */
463
        public void setPosOffset(double lonOffset, double latOffset) {
464
                        this.lonOffset = lonOffset;
465
                        this.latOffset = latOffset;
466
        }
467
        
468
        /**
469
         * The very last position that the driver has delivered to the listeners. 
470
         * @return point2D
471
         */
472
        public Point2D getCurrentPosition() {
473
                return currentPos;
474
        }
475

    
476
        /**
477
         * Returns the current sample rate set.
478
         * @return
479
         */
480
        public int getSampleRate() {
481
                return rate;
482
        }
483
        
484
        public static void main(String[] args) {
485
                if (args.length < 4) {
486
                        System.out.print("GPSReader port portSpeed\n");
487
                        System.exit(-1);
488
                }
489
                Enumeration portList = CommPortIdentifier.getPortIdentifiers();
490
                while (portList.hasMoreElements()) {
491
                        CommPortIdentifier myPortId = (CommPortIdentifier) portList.nextElement();
492
                        if (myPortId.getPortType() == CommPortIdentifier.PORT_SERIAL) {
493
                                if (myPortId.getName().equals(args[0])) {
494
                                        try {
495
                                                GPSDriver driver = GPSDriver.getInstance();
496
                                                driver.setPort(myPortId, Integer.parseInt(args[1]), SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
497
                                                driver.setSampleRate(100);
498
                                                driver.addEventListener(new GPSEventListener() {
499
                                                        public void unhandledMessage(String msg) {
500
                                                                System.out.println("UNHANDLED: "+msg);
501
                                                        }
502

    
503
                                                        public void connectionLost() {
504
                                                                System.out.println("CONNECTION LOST");        
505
                                                        }
506

    
507
                                                        public void connectionEstablished() {
508
                                                                System.out.println("CONNECTION ESTABLISHED");
509
                                                        }
510

    
511
                                                        public void newLonLatPositionReceived(double lon, double lat) {
512
                                                                System.out.println("NEW LONLAT POSITION RECEIVED: ("+lon+", "+lat+")");
513
                                                        }
514

    
515
                                                        public void signalQualityChanged(float level, int satellites, String qualityStatus) {
516
                                                                System.out.println("NEW SIGNAL LEVEL: "+level+" ("+satellites+" satellites in view)");
517
                                                        }
518

    
519
                                                        public void speedChanged(float speed, short course) {
520
                                                                System.out.println("SPEED/COURSE CHANGED: ( "+speed+" Km/h, "+course+" degrees)");
521
                                                        }
522

    
523
                                                        public void estimatedPosErrorChanged(double e) {
524
                                                                System.out.println("ESTIMATED POSITION ERROR CHANGED: "+e);
525
                                                        }
526

    
527
                                                        public void altitudeChanged(float height) {
528
                                                                System.out.println("HEIGHT CHANGED: "+height);
529
                                                        }
530

    
531
                                                        public void precisionChanged(float pDop, float hDop, float vDop) {
532
                                                                System.out.println("PRECISION CHANGED: "+pDop+", "+hDop+", "+vDop+")");
533
                                                        }
534
                                                        
535
                                                });
536
                                                driver.connect();
537
                                        } catch (PortInUseException e) {
538
                                                System.err.println("Port busy");
539
                                        }
540
                                        
541
                                }
542
                        }
543
                }
544
        }
545

    
546
}