Source code: jflight/gps/garmin/GarminGps.java
1 /*
2 Project name: JFlight
3 Hosted at: www.sourceforge.net
4 Homepage: jflight.sourceforge.net
5 Licence: GNU public licence (GPL)
6 Filename: GarminGps.java
7 Package: jflight
8 */
9
10 package jflight.gps.garmin;
11 import jflight.gps.*;
12 import jflight.model.*;
13
14 import java.io.*;
15 import java.util.*;
16 import java.text.*;
17 import java.awt.*;
18 import java.awt.event.*;
19 import java.lang.System;
20
21 /**
22 * Support for the Garmin GPS-devices.
23 *
24 *
25 *
26 * @since JDK1.1.x
27 * @author R?diger Bien
28 *
29 * CVS-section:
30 * @file_version $Revision: 1.38 $
31 *
32 */
33 public class GarminGps
34 extends Gps {
35
36
37
38 private int pid;
39 private int bufSize;
40 private byte dataBuf[];
41 //private byte varioBuf[];
42 //private int varioCnt;
43 private Caps GpsCaps;
44 private byte msg[];
45
46
47 // L001 - link protocol 1
48 private final int PID_ACK = 6;
49 private final int PID_NACK = 21;
50 private final int PID_CMD_DATA = 10;
51 private final int PID_XFER_CMPLT = 12;
52 private final int PID_RECORDS = 27;
53 private final int PID_WPT_DATA = 35;
54 private final int PID_TRK_DATA = 34;
55 private final int PID_RTE_WPT_DATA = 30;
56 private final int PID_RTE_HDR = 29;
57 private final int PID_RTE_LNK = 98;
58 private final int PID_TRK_HDR = 99;
59 private final int PID_PROD_REQ = 254;
60 private final int PID_PROD_DATA = 255;
61
62 // A010 - device command protocol 1
63 private final int CMD_START_PVT_DATA = 49;
64 private final int CMD_STOP_PVT_DATA = 50;
65 private final int CMD_TRANSFER_WPT = 7;
66 private final int CMD_TRANSFER_TRK = 6;
67 private final int CMD_TRANSFER_TIME = 5;
68 private final int CMD_TRANSFER_RTE = 4;
69 private final int CMD_TRANSFER_PRX = 3;
70 private final int CMD_TRANSFER_POS = 2;
71
72 private final int DLE = 16;
73 private final int ETX = 3;
74 private final int RX_TIMEOUT = 500; // # of ms
75 private final int TX_RETRY = 10;
76 private final String Delim = ",|";
77 private final int WPT_MODE = 0;
78 private final int RTE_MODE = 1;
79
80
81 private final int BUG_WORKAROUND = 60*60*1000;
82 private final int MAX_INT = 2147483647;
83
84 /** Capabilities of selected Garmin GPS
85 *
86 * Some Garmin devices behave different depending on the version. The interval
87 * can be set in sw_version_beg / _end.
88 *
89 */
90
91 // id, sw-version beg, sw-version end, data-formats for wpt, rte, trk, prx
92 // 0 means not supported
93 private final int GpsCapsList[][]= {
94 // GPS 12-types:
95 { 0, 1, 0xFFFF, 100, 201, 300, 0 }, // default-dummy
96 { 77, 1, 301, 100, 201, 300, 400 },
97 { 77, 301, 350, 103, 201, 300, 403 },
98 { 77, 350, 361, 103, 201, 300, 0 },
99 { 77, 361, 0xFFFF, 103, 201, 300, 403 },
100 { 87, 1, 0xFFFF, 103, 201, 300, 403 },
101 { 96, 1, 0xFFFF, 103, 201, 300, 403 },
102 { 105, 1, 0xFFFF, 103, 201, 300, 403 },
103 { 106, 1, 0xFFFF, 103, 201, 300, 403 },
104 { 138, 1, 0xFFFF, 103, 201, 300, 403 },
105 // GPS II:
106 { 59, 1, 0xFFFF, 100, 201, 300, 0 },
107 // GPS 45:
108 { 31, 1, 0xFFFF, 100, 201, 300, 0 },
109 { 41, 1, 0xFFFF, 100, 201, 300, 0 },
110 // GPS III:
111 //{ 72, 1, 0xFFFF, 104, 201, 300, 0 },
112 // GPS II+:
113 { 73, 1, 0xFFFF, 103, 201, 300, 0 },
114 { 97, 1, 0xFFFF, 103, 201, 300, 0 },
115 // ETREX-Family: (ID & formats to be checked!)
116 { 0/*?*/, 1, 0xFFFF, 109, 201, 301/*the only one with altitude*/, 0 },
117 };
118 private final int NUM_GPS = 16; // update this number with every new GPS !!!
119 private final int DEFAULT_GPS = 0;
120
121
122 /**
123 * Constructor GarminGps
124 *
125 */
126 public GarminGps() {
127
128 super();
129
130 msg = new byte[300];
131 dataBuf = new byte[400]; // here a single packet will be stored for later evaluation
132 GpsCaps = new Caps();
133 //varioCnt = 0;
134 ioInit = false;
135
136 if ((debug&0x01)!=0) printDebug("Init GarminGps");
137
138 this.gpsID = "GARXXX";
139
140 }
141
142
143 public String getManufactoringID() {
144 return "4";
145 }
146
147
148 /**
149 * Send a packet with a variable size to the GPS
150 * If an IOException occurs, it retries some time, then rethrows the Exception
151 * Wait for (n)ack, if required
152 * Resends packet on NACK until too much retries
153 * Uses class-variable "msg"
154 *
155 * @return SUCCESS implies ACK has been received <br>
156 * TX_RETRY_ERROR if too much NACKs have been received and packet sent <br>
157 * was no ACK/NACK <br>
158 * IO_NOT_INITIALIZED
159 *
160 * @exception GpsTimeoutException ...
161 * @exception IOException ...
162 */
163 private int sendVarPacket( byte packetId, byte data[], byte num)
164 throws GpsTimeoutException, IOException {
165 int i=0;
166 int j = 0;
167 int crc = 0;
168 int ret = SUCCESS;
169 long t1;
170 boolean err = true;
171 int retry = 0;
172
173 if( !ioInit ) return IO_NOT_INITIALIZED;
174 if((debug&0x01)!=0) printDebug("GarminGPS::sendPacket():");
175 // assemble packet:
176 msg[i++] = DLE;
177 msg[i++] = packetId;
178 msg[i++] = num;
179 if( num == DLE ) {
180 msg[i++] = DLE;
181 }
182 crc = absByte(packetId) + absByte(num);
183 if( num > 0 ) {
184 if((debug&0x01)!=0) printDebug(" data 0,1 (i)= " + data[0] + " " + data[1] + " " + i + "---\n");
185 for( j=0; j<num; j++ ) {
186 msg[i++] = data[j];
187 crc += absByte(data[j]);
188 if( data[j] == DLE ) {
189 msg[i++] = DLE;
190 }
191 }
192 }
193 if((debug&0x01)!=0) printDebug(" crc = " + crc + "---");
194 crc = (~crc)+1;
195 msg[i++] = (byte) (crc&0x00ff);
196 if((debug&0x01)!=0) printDebug(" crc k2 = " + (crc&0x00ff) + "---");
197 if( (crc&0x00ff) == DLE ) {
198 msg[i++] = DLE;
199 }
200 msg[i++] = DLE;
201 msg[i++] = ETX;
202 if((debug&0x01)!=0) {
203 printDebug("num bytes: " + i + " ---");
204 for( j=0; j<(i); j++) printDebug("#" + ((int)msg[j]) + ",");
205 }
206
207 // packet is assembled, now send it:
208 t1 = System.currentTimeMillis();
209 do {
210 try {
211 err = false;
212 os.write(msg, 0, i );
213 os.flush();
214 } catch (IOException e) {
215 // data may have been sent or not, don't know
216 // if sent, resend should't disturb
217 // if not, retry is required
218 if( (System.currentTimeMillis() - t1) > RX_TIMEOUT ) {
219 if((debug&0x01)!=0) printDebug("OutputStream write error: " + e);
220 throw new IOException(e.getMessage()); // rethrow Exception
221 }
222 else {
223 err = true;
224 }
225 }
226 if(!err) {
227 if( (packetId != PID_ACK) && (packetId != PID_NACK) ) {
228 // data have been sent, but maybe not received by the GPS -> wait for (N)ACK:
229 t1 = System.currentTimeMillis(); // restart timer
230 do {
231 getPacket(false); // timeout? tbd
232 }
233 while( (pid != PID_ACK) && (pid != PID_NACK) );
234 if( pid != PID_ACK ) {
235 if( retry++ > TX_RETRY ) ret = TX_RETRY_ERROR;
236 else err = true; // retry
237 }
238
239 } // (packetId != PID_ACK) && (packetId != PID_NACK)
240 } // if(!err)
241 }
242 while( err );
243 if((debug&0x01)!=0) printDebug(" end send var packet ---");
244 return ret;
245 }
246
247
248
249 /**
250 * Get packet
251 * Shall be able to deal with an unstable or lost connection. What may happen:
252 * - There is no connection at all: E.g. wrong interface setup -> timeout detection
253 * - The connection gets lost: No more chars come in -> timeout detection
254 * - The connection is unstable: Some chars are missing -> the sequence 0x10 - 0x03
255 * occurs out-of-order, timeout detection
256 * In any case only a single packet (or two, if the devices switch their
257 * client-server releationship) can get lost.
258 * Detecting a wrong byte-sequence is quite difficult and not implemented so far,
259 * just timeout detection.
260 * If a timout occurs, the missing bytes will not be resent. In case of (N)ACK, some
261 * GPS will resend it after 2-5s, but this cannot be relied on. To be sure
262 * a NAVCK should be assumed an the last transfer should be done again.
263 * In case of an interrupted data-transfer a NACK should be sent by the PC to request
264 * retransmission.
265 *
266 * changes: pid, dataBuf, bufSize
267 * @param idMode: Packet A001 during ID-reading may come or not, no exception shall be thrown here
268 * @return SUCCESS or
269 * BAD_CRC: CRC-error <br>
270 * IO_NOT_INITIALIZED <br>
271 * data in buffer dataBuf <br>
272 *
273 * @exception GpsTimeoutException ...
274 * @exception IOException ...
275 */
276 private int getPacket(boolean idMode) throws IOException, GpsTimeoutException {
277 int state = 0;
278 int b; // in byte
279 int stuffCnt = 0; // count DLE stuffing
280 int crc = -1;
281 int sum = 0; // for crc calculation
282 int ret = 0;
283 boolean stuff = false;
284 int userDataCnt = 0;
285 long t1;
286
287 if( !ioInit ) return IO_NOT_INITIALIZED;
288 if((debug&0x01)!=0) printDebug("GarminGPS::getPacket(): waiting for one packet from GPS ...");
289
290 t1 = System.currentTimeMillis();
291 while( state != 99 ) {
292 if((debug&0x01)!=0) printDebug(" [" + state + "] " );
293 b = is.read();
294 if (b == -1 ) {
295 if ((System.currentTimeMillis() - t1) > RX_TIMEOUT ) {
296 if( idMode ) {
297 return GPS_TIMEOUT;
298 }
299 else if ((debug&0x01)!=0) printDebug("GarminGPS::getPacket(): timeout!");
300 throw new GpsTimeoutException("Timeout: GPS doesn't reply");
301 } else {
302 delay(1);
303 continue;
304 }
305 }
306 if((debug&0x01)!=0) printDebug(" *" + b);
307 switch (state) {
308 /* DLE */
309 case 0:
310 if( b != 0x10 ) {
311 state = 9; // sequence error
312 }
313 else {
314 state++;
315 }
316 break;
317 /* type */
318 case 1:
319 pid = absByte((byte)b);
320 sum += pid;
321 state++;
322 break;
323 /* size */
324 case 2:
325 bufSize = b;
326 sum += absByte((byte)bufSize);
327 if( bufSize == 0x10 ) {
328 state++;
329 } else {
330 state+=2;
331 }
332 break;
333 // read stuff byte
334 case 3:
335 state++;
336 break;
337 /* user data */
338 case 4:
339 dataBuf[userDataCnt-stuffCnt] = (byte) b;
340 if( b == 0x10 ) {
341 if( stuff == false ) {
342 stuff = true;
343 stuffCnt++;
344 sum += absByte((byte)b);
345 } else stuff = false;
346 } else sum += absByte((byte)b);
347 userDataCnt++;
348 if( userDataCnt >= (bufSize+stuffCnt)) {
349 state++;
350 }
351 break;
352 /* crc */
353 case 5:
354 crc = b;
355 if( crc == 0x10 ) {
356 state++;
357 } else {
358 state += 2;
359 }
360 // crc ok ?
361 if( (((sum&0x00FF) + absByte((byte)crc))&0x00FF) == 0 ) {
362 ret = SUCCESS;
363 if((debug&0x01)!=0) printDebug(" crc ok " + crc);
364 } else {
365 ret = BAD_CRC;
366 if((debug&0x01)!=0) printDebug(" crc bad " + sum + " " + crc);
367 }
368 break;
369 // read stuff byte
370 case 6:
371 state++;
372 break;
373 /* DLE */
374 case 7:
375 state++;
376 break;
377 /* ETX */
378 case 8:
379 state=99; // end
380 break;
381 // simple escape from sequence error: wait for DLE-ETX sequence
382 case 9:
383 if( b == 0x10 ) state = 10;
384 break;
385 case 10:
386 if( b == 0x03 ) {
387 throw new GpsTimeoutException("Fatal data sequence error! May require power off/on on GPS to escape");
388 }
389 break;
390 } // switch
391 } // while
392
393 //System.out.println("Pid: " + pid );
394
395 if((debug&0x01)!=0) printDebug("- packet end -");
396 if((debug&0x01)!=0) printDebug("GarminGPS::getPacket() Finished normally with ret = " + ret);
397 return ret;
398 }
399
400 /**
401 * Identify the GPS connected and return some descriptive information.
402 * For the main class this function is for GPS-type detection (in case
403 * other devices are supported too) and informative use. <br>
404 * Modifies <a href="jflight.gps.Gps.html#gpsId">gpsId</a>,
405 * <a href="jflight.gps.Gps.html#swVersion">swVersion</a>,
406 * <a href="jflight.gps.Gps.html#prodDescr">prodDescr</a>
407 *
408 * @return SUCCESS <br>
409 * BAD_CRC: CRC-error <br>
410 * TX_RETRY_ERROR if too much NACKs have been received; <br>
411 * IO_NOT_INITIALIZED
412 *
413 *
414 * @exception GpsTimeoutException ...
415 * @exception IOException ...
416 */
417 public int getGpsId() throws GpsTimeoutException, IOException {
418 int ret;
419 int i;
420 byte tag;
421 short data;
422
423 if((debug&0x01)!=0) System.err.println("getGpsId():");
424 if( !ioInit ) return IO_NOT_INITIALIZED;
425
426 ret = sendVarPacket( (byte) 254, msg, (byte) 0 );
427 if( ret != SUCCESS ) return ret;
428 // must have been ACK
429
430 // A000-type data
431 ret = getPacket(false);
432 if((debug&0x01)!=0) printDebug("data pid: " + pid + " ---");
433 if( ret != SUCCESS ) return ret;
434
435 // setup input stream to read data from buffer:
436 DataInputStream in =
437 new DataInputStream(
438 new BufferedInputStream(
439 new ByteArrayInputStream(dataBuf)));
440
441 gpsId = in.readShort();
442 gpsId = swap16(gpsId);
443 swVersion = in.readShort();
444 swVersion = swap16(swVersion);
445
446 if( bufSize > 4 ) {
447 prodDescr = new String( dataBuf, 4, bufSize-5 );
448 prodDescr.trim();
449 } else {
450 prodDescr = new String("Garmin GPS");
451 }
452 byte[] locbuf = new byte[2];
453 locbuf[0] = (byte)PID_PROD_DATA;
454 locbuf[1] = (byte)0;
455 ret = sendVarPacket( (byte) PID_ACK, locbuf, (byte)2); // ACK
456
457 // A001 packet ? Check, if a packet is sent by the GPS
458 if( getPacket(true) == SUCCESS ) {
459 ret = sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
460 //if((debug&0x01)!=0) printDebug("A001 packet received ---");
461 //System.out.println("A001 packet received ---");
462 // setup input stream to read data from buffer:
463 in =
464 new DataInputStream(
465 new BufferedInputStream(
466 new ByteArrayInputStream(dataBuf)));
467
468 int aValue = 0;
469 for(int a=0, d=0;;) {
470 tag = in.readByte();
471 switch (tag) {
472 case 'P':
473 data = in.readShort(); // ignore
474 data = swap16(data);
475 //System.out.println("P " + data);
476 break;
477 case 'L':
478 data = in.readShort(); // ignore
479 data = swap16(data);
480 //System.out.println("L " + data);
481 break;
482 case 'A':
483 a++;
484 data = in.readShort();
485 data = swap16(data);
486 aValue = data;
487 //System.out.println("A " + data);
488 switch(a) {
489 case 1:
490 break; // ignore the first one
491 case 2:
492 GpsCaps.wpt_a = data;
493 break;
494 case 3:
495 GpsCaps.rte_a = data;
496 break;
497 case 4:
498 GpsCaps.trk_a = data;
499 break;
500 default:
501 break;
502 }
503 break;
504 case 'D':
505 data = in.readShort();
506 data = swap16(data);
507 //System.out.println("D " + data);
508 switch(a) {
509 // ignore the first one
510 case 2:
511 GpsCaps.wpt_d = data;
512 break;
513 case 3:
514 GpsCaps.rte_d[d++] = data;
515 break;
516 case 4:
517 GpsCaps.trk_d = data;
518 break;
519 default:
520 break;
521 }
522 break;
523 }
524 if( aValue > 300 ) break; // other protocols are not handled anyway
525 } // for-loop
526 }
527 else {
528 for( i=0; i<NUM_GPS; i++ ) {
529 if( GpsCapsList[i][0] == gpsId ) {
530 if( (swVersion >= GpsCapsList[i][1]) && (swVersion < GpsCapsList[i][2]) ) {
531 if((debug&0x01)!=0) printDebug("Device supported!");
532 break;
533 }
534 }
535 }
536 if( i == NUM_GPS ) {
537 if((debug&0x01)!=0) printDebug("Device with ID " + gpsId + " not supported!");
538 if((debug&0x01)!=0) printDebug("Using default-device (GPS-12)");
539 i = DEFAULT_GPS; // default-device
540 }
541
542 GpsCaps.wpt_d = GpsCapsList[i][3];
543 GpsCaps.rte_d[0] = GpsCapsList[i][4];
544 GpsCaps.rte_d[1] = 103; // fixed
545 GpsCaps.trk_d = GpsCapsList[i][5];
546 GpsCaps.prx_d = GpsCapsList[i][6];
547 GpsCaps.wpt_a = 100; // fixed
548 GpsCaps.rte_a = 200; // fixed
549 GpsCaps.trk_a = 300; // fixed
550 }
551
552 if((debug&0x01)!=0) {
553 printDebug("GPS ID: " + gpsId );
554 printDebug("SW Version: " + swVersion );
555 printDebug("GPS description: " + prodDescr );
556 }
557
558 System.out.println( GpsCaps );
559 return ret;
560 }
561
562
563 /*
564 D109 Waypoint Type handling by JFlight: Simplified, mapped to the D103-capabilities
565
566 typedef struct // size // JFlight:
567 {
568 byte dtyp; // data packet type (0x01 for D109)1 //
569 byte wpt_class; // class 1 // 0: user-wpt
570 byte dspl_color; // display & color (see below) 1 // 0: black
571 byte attr; // attributes (0x70 for D109) 1 //
572 Symbol_Type smbl; // waypoint symbol 2 // -1: dot
573 byte subclass[18]; // subclass 18 // 6*0x00, 12*0xFF
574 Semicircle_Type posn; // 32 bit semicircle 8 // D103
575 float alt; // altitude in meters 4 // 0.0
576 float dpth; // depth in meters 4 // 0.0
577 float dist; // proximity distance in meters 4 // 0.0
578 char state[2]; // state 2 // " "
579 char cc[2]; // country code 2 // " "
580 longword ete; // outbound link ete in seconds 4 // -1
581 (52 bytes so far)
582 // char ident[]; variable length string 1-51 // D103, first 6 letters
583 // char comment[]; waypoint user comment 1-51 // D103 comment
584 // char facility[]; facility name 1-31 // empty (0-terminated)
585 // char city[]; city name 1-25 // empty (0-terminated)
586 // char addr[]; address number 1-51 // empty (0-terminated)
587 // char cross_road[]; intersecting road label 1-51 // empty (0-terminated)
588 } D109_Wpt_Type;
589
590 */
591 /**
592 * Read a single waypoint from the input stream and save it in curWpt
593 * Data types D108/109 are required for devices passing altitude too
594 *
595 * @return curWpt (module local variable)
596 *
597 * @exception IOException ...
598 */
599 private void getWpt(DataInputStream in, int rteWptMode) throws IOException {
600 int i,
601 lat,
602 lon,
603 identOffset;
604 int accum = 0;
605 int len=0, len_tmp, offs;
606 float height;
607 int caps;
608
609 if( rteWptMode == WPT_MODE ) {
610 caps = GpsCaps.wpt_d;
611 }
612 else {
613 caps = GpsCaps.rte_d[1];
614 }
615
616 curWpt = new Wpt();
617 switch( caps ) {
618 case 100:
619 case 101:
620 case 102:
621 case 103:
622 case 104:
623 case 107:
624 curWpt.name = new String( dataBuf, 0, 6 );
625 // eleminate the string-terminating 0-char:
626 for( i=18; i<58; i++ ) {
627 if( dataBuf[i] == 0 ) dataBuf[i] = 0x20;
628 }
629 curWpt.comment = new String( dataBuf, 18, 40 );
630 curWpt.comment = curWpt.comment.trim();
631 in.skipBytes(6);
632 lat = in.readInt();
633 lat = swap32(lat);
634 lon = in.readInt();
635 lon = swap32(lon);
636 curWpt.dlat = ((double)lat*180)/MAX_INT;
637 curWpt.dlon = ((double)lon*180)/MAX_INT;
638 if( GpsCaps.wpt_d == 103 ) {
639 in.skipBytes(44);
640 curWpt.iconId = in.readByte();
641 curWpt.displayOption = in.readByte();
642 }
643 break;
644 case 108:
645 case 109:
646 // default-values:
647 curWpt.iconId = Wpt.SYM_DOT;
648 // values from the GPS:
649 in.skipBytes(24);
650 lat = in.readInt();
651 lat = swap32(lat);
652 lon = in.readInt();
653 lon = swap32(lon);
654 curWpt.dlat = ((double)lat*180)/MAX_INT;
655 curWpt.dlon = ((double)lon*180)/MAX_INT;
656
657 // height:
658 accum = in.readInt();
659 accum = swap32(accum);
660 height = Float.intBitsToFloat(accum);
661 if( height == 1.0e25 ) { // not supported by device or unknown
662 curWpt.height = 0;
663 } else {
664 curWpt.height = (int) height;
665 }
666
667 if( GpsCaps.wpt_d == 108 ) identOffset = 48;
668 else identOffset = 52;
669
670 // ident string:
671 if( dataBuf[identOffset] != 0 ) {
672 for( i=0, len=0; i<identOffset; i++ ) {
673 if( dataBuf[identOffset+i] != 0 ) len++;
674 else break;
675 }
676 if( len > 6 ) len_tmp = 6;
677 else len_tmp = len;
678 curWpt.name = new String( dataBuf, identOffset, len_tmp );
679 }
680 len++; // 0-byte
681
682 // comment string:
683 offs = len;
684 if( dataBuf[identOffset+offs] != 0 ) {
685 for( i=0, len=0; i<identOffset; i++ ) {
686 if( dataBuf[identOffset+offs+i] != 0 ) len++;
687 else break;
688 }
689 }
690 if( len > 40 ) len_tmp = 40;
691 else len_tmp = len;
692 curWpt.comment = new String( dataBuf, identOffset+offs, len_tmp );
693 curWpt.comment = curWpt.comment.trim();
694 System.out.println(GpsCaps.wpt_d + ": " + curWpt );
695 // done d108/109 ;-)
696 break;
697 default:
698 throw new IOException("Waypoint data type" + GpsCaps.wpt_d +" not supported");
699
700 }
701
702 if((debug&0x01)!=0) {
703 if((debug&0x01)!=0) printDebug("\nWPT name: " + curWpt.name);
704 if((debug&0x01)!=0) printDebug("WPT comment: " + curWpt.comment);
705 if((debug&0x01)!=0) printDebug("WPT dlat/dlon: " + curWpt.dlat + " - " +
706 curWpt.dlon);
707 if((debug&0x01)!=0) printDebug("WPT opt/sym: " + curWpt.displayOption + " - " +
708 curWpt.iconId);
709 }
710 }
711
712 /**
713 * Read all waypoints from the GPS and store them locally.
714 *
715 * @return SUCCESS <br>
716 * BAD_CRC: CRC-error <br>
717 * TX_RETRY_ERROR if too much NACKs have been received; <br>
718 * IO_NOT_INITIALIZED
719 *
720 * @param mode: Gps.REPLACE or Gps.APPEND waypoints to the list
721 *
722 * @exception GpsTimeoutException ...
723 * @exception IOException ...
724 */
725 public int getWpts(int mode) throws GpsTimeoutException, IOException {
726 int ret;
727 int i;
728 int numPacks;
729 byte locBuf[] = new byte[2];
730 int pidTmp = PID_WPT_DATA;
731 int size;
732
733 if( !ioInit ) return IO_NOT_INITIALIZED;
734 if( gpsId == 0 ) {
735 // make sure Gps has been detetced:
736 ret = getGpsId();
737 if( ret != SUCCESS ) return ret;
738 } else {
739 // send NACK; may help to recover from interrupted transmissions:
740 sendVarPacket( (byte) PID_NACK, msg, (byte) 0 );
741 }
742
743
744
745 // send request to GPS: send wpts
746 locBuf[0] = CMD_TRANSFER_WPT;
747 ret = sendVarPacket( (byte) PID_CMD_DATA, locBuf, (byte) 2 );
748 if((debug&0x01)!=0) printDebug("ret: " + ret + " ---");
749 if( ret != SUCCESS ) return ret;
750
751 // GPS sends PID_RECORDS and num packets to come:
752 ret = getPacket(false);
753 if( ret != SUCCESS ) return ret;
754 if((debug&0x01)!=0) {
755 printDebug("data pid (expected: 27): " + pid + " ---");
756 printDebug("num packets: " + absByte(dataBuf[0]) + " " + absByte(dataBuf[1]));
757 }
758 numPacks = absByte(dataBuf[0]) + (absByte(dataBuf[1])<<8);
759 if((debug&0x01)!=0) printDebug("num packets to come: " + numPacks + " ---");
760 // confirm data:
761 sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
762
763 // read wpts:
764 if( mode == Gps.REPLACE ) {
765 wptCont.removeAllWpts();
766 }
767 if( numPacks > 0 )
768 while( pidTmp == PID_WPT_DATA ) {
769 i = 0;
770 ret = getPacket(false);
771 pidTmp = pid;
772 if( ret == SUCCESS ) {
773 // confirm data:
774 sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
775 }
776 else {
777 sendVarPacket( (byte) PID_NACK, msg, (byte) 0 ); // NACK
778 continue;
779 }
780
781 // analyze the data:
782 if( pidTmp != PID_XFER_CMPLT ) {
783 // setup input stream to read data from buffer:
784 DataInputStream in =
785 new DataInputStream(
786 new BufferedInputStream(
787 new ByteArrayInputStream(dataBuf)));
788 getWpt(in, WPT_MODE);
789 if( mode == Gps.APPEND ) {
790 // insertNewWpt( curWpt );
791 // now check, if waypoint (checked by name) already exists:
792 String newWptName = curWpt.getName();
793 size = wptCont.size();
794 for( i=0; i<size; i++) {
795 Wpt aWpt = wptCont.wptAt(i);
796 if( aWpt.getName().compareTo(newWptName) == 0 ) {
797 if( aWpt.getComment().compareTo(curWpt.getComment()) == 0 ) {
798 break;
799 }
800 }
801 }
802 if( i == size ) {
803 // it's a new Wpt; now insert it at the right position:
804 for( i=0; i<size; i++) {
805 Wpt aWpt = wptCont.wptAt(i);
806 if( aWpt.getName().compareTo(newWptName) < 0 ) continue;
807 else {
808 wptCont.insertWptAt(curWpt,i);
809 break;
810 }
811 }
812 // insert at the end?
813 if( i == size ) {
814 wptCont.addWpt(curWpt);
815 }
816 }
817 } else { // REPLACE mode
818 // Wpts already sorted by the GPS: just append it
819 wptCont.addWpt( curWpt );
820 }
821 }
822 }
823 else {
824 if((debug&0x01)!=0) printDebug("no waypoints on GPS ---");
825 }
826
827 return ret;
828 }
829
830
831 /** send a single waypoint, without any header- or end-package:
832 * @exception GpsTimeoutException ...
833 * @exception IOException ...
834 */
835 private int sendASingleWpt(int index, byte packetId, int rteWptMode)
836 throws IOException, GpsTimeoutException {
837 int i, l, ret,
838 lat,
839 lon;
840 double dlat,
841 dlon;
842 float height;
843 Wpt wpt;
844 byte length=0;
845 byte[] locBuf2 = new byte[300];
846 int caps;
847
848 wpt = wptCont.wptAt(index);
849
850 // setup input stream to write data to buffer:
851 ByteArrayOutputStream locBuf = new ByteArrayOutputStream(300);
852 DataOutputStream out = new DataOutputStream( locBuf );
853
854 if( rteWptMode == WPT_MODE ) {
855 caps = GpsCaps.wpt_d;
856 }
857 else {
858 caps = GpsCaps.rte_d[1];
859 }
860
861 // build buffer:
862 switch( caps ) {
863 case 100:
864 case 103:
865 out.writeBytes(wpt.name);
866 if( (l=wpt.getName().length()) < 6 ) {
867 for( i=0; i<(6-l); i++ ) out.writeByte(32);
868 }
869 dlat = (wpt.dlat/180)*MAX_INT;
870 dlon = (wpt.dlon/180)*MAX_INT;
871 lat = (int) dlat;
872 lon = (int) dlon;
873 out.writeInt(swap32(lat));
874 out.writeInt(swap32(lon));
875 out.writeInt(0); // dummy
876 out.writeBytes(wpt.comment);
877 if( (l=wpt.getComment().length()) < 40 ) {
878 for( i=0; i<(40-l); i++ ) out.writeByte(32);
879 }
880 length = 58;
881 if( GpsCaps.wpt_d == 103 ) {
882 out.writeByte(wpt.iconId);
883 out.writeByte(wpt.displayOption);
884 length += 2;
885 }
886 locBuf2[57] = 0;
887 break;
888 case 108:
889 out.writeByte(0x00);
890 out.writeByte(0xFF); // def. color
891 out.writeByte(0x00);
892 out.writeByte(0x60); // attr
893 out.writeShort(0xFFFF); // symbol; will be set to "dot" (default) by GPS
894 out.writeShort(0x0000); // subclass
895 out.writeShort(0x0000);
896 out.writeShort(0x0000);
897 out.writeShort(0xFFFF);
898 out.writeShort(0xFFFF);
899 out.writeShort(0xFFFF);
900 out.writeShort(0xFFFF);
901 out.writeShort(0xFFFF);
902 out.writeShort(0xFFFF);
903 dlat = (wpt.dlat/180)*MAX_INT;
904 dlon = (wpt.dlon/180)*MAX_INT;
905 lat = (int) dlat;
906 lon = (int) dlon;
907 out.writeInt(swap32(lat));
908 out.writeInt(swap32(lon));
909 height = wpt.height;
910 out.writeInt(swap32(Float.floatToIntBits(height)));
911 height = 1.0F+25;
912 out.writeInt(swap32(Float.floatToIntBits(height)));
913 height = 0;
914 out.writeInt(swap32(Float.floatToIntBits(height)));
915 for(int loop = 0;loop<4;loop++) out.writeByte((byte)'X'); // state, country code
916
917 out.writeBytes(wpt.name);out.writeByte(0x00);
918 out.writeBytes(wpt.comment);out.writeByte(0x00);
919
920 out.writeByte(0x00);
921 out.writeByte(0x00);
922 out.writeByte(0x00);
923 out.writeByte(0x00);
924
925 length = (byte)out.size();
926 break;
927 case 109:
928 out.writeByte(0x01);
929 out.writeByte(0x00); // user wpt
930 out.writeByte(0x1F); // color
931 out.writeShort(0xFFFF); // will be set to "dot" (default) by GPS
932 out.writeShort(0x0000); // subclass
933 out.writeShort(0x0000);
934 out.writeShort(0x0000);
935 out.writeShort(0xFFFF);
936 out.writeShort(0xFFFF);
937 out.writeShort(0xFFFF);
938 out.writeShort(0xFFFF);
939 out.writeShort(0xFFFF);
940 out.writeShort(0xFFFF);
941 dlat = (wpt.dlat/180)*MAX_INT;
942 dlon = (wpt.dlon/180)*MAX_INT;
943 lat = (int) dlat;
944 lon = (int) dlon;
945 out.writeInt(swap32(lat));
946 out.writeInt(swap32(lon));
947 height = wpt.height;
948 out.writeInt(swap32(Float.floatToIntBits(height)));
949 height = 1.0F+25;
950 out.writeInt(swap32(Float.floatToIntBits(height)));
951 height = 0;
952 out.writeInt(swap32(Float.floatToIntBits(height)));
953 for(int loop = 0;loop<4;loop++) out.writeByte((byte)'X'); // state, country code
954 out.writeInt(-1);
955
956 out.writeBytes(wpt.name);
957 out.writeByte(0x00);
958
959 out.writeBytes(wpt.comment);
960 out.writeByte(0x00);
961
962 out.writeByte(0x00);
963 out.writeByte(0x00);
964 out.writeByte(0x00);
965 out.writeByte(0x00);
966 length = (byte)out.size();
967 break;
968 // send waypoint:
969 default:
970 throw new IOException("Error: Waypoint data type " + GpsCaps.wpt_d+ " not yet supported!"); // rethrow Exception
971 }
972 locBuf2 = locBuf.toByteArray(); // get buffer
973 ret = sendVarPacket( packetId, locBuf2, length );
974
975 return ret;
976 }
977
978 /**
979 * Sends one or all waypoint(s) to the GPS. Uses the waypoint with the specified index
980 * in the Vector of all waypoints. If index is equalt to -1, then all wpts are sent. <br>
981 * Note: It's important that the comment is null-terminated
982 * (doesn't match the garmin description).
983 *
984 * @param index Index of the waypoint in the list
985 * @return SUCCESS <br>
986 * BAD_CRC: CRC-error <br>
987 * TX_RETRY_ERROR if too much NACKs have been received <br>
988 * WRONG_INDEX_ERROR if waypoint doesn't exist <br>
989 * IO_NOT_INITIALIZED
990 *
991 * @exception GpsTimeoutException ...
992 * @exception IOException ...
993 */
994 public int sendWpt(int index) throws GpsTimeoutException, IOException {
995 int ret;
996 int i;
997
998 byte[] packs = new byte[2];
999 int numWpts;
1000
1001 if( !ioInit ) return IO_NOT_INITIALIZED;
1002 if( gpsId == 0 ) {
1003 // make sure Gps has been detetced:
1004 ret = getGpsId();
1005 if( ret != SUCCESS ) return ret;
1006 } else {
1007 // send NACK; may help to recover from interrupted transmissions:
1008 // makes problems when sending single waypoints repeatedly:
1009 //sendVarPacket( (byte) PID_NACK, msg, (byte) 0 );
1010 }
1011
1012 // get wpt from the list:
1013 numWpts = wptCont.size();
1014 if( (index > numWpts) || (numWpts == 0) ) return WRONG_INDEX_ERROR;
1015
1016 if( index >= 0 ) {
1017 // send number of records to follow to the GPS
1018 packs[0] = 1; // data packet(s) to follows
1019 packs[1] = 0;
1020 ret = sendVarPacket( (byte) PID_RECORDS, packs, (byte) 2 );
1021 if( ret != SUCCESS ) return ret;
1022
1023 sendASingleWpt( index, (byte)PID_WPT_DATA, WPT_MODE );
1024
1025 } else {
1026 // send number of records to follow to the GPS
1027 packs[0] = (byte) (numWpts&0xFF); // data packet(s) to follows
1028 packs[1] = (byte) ((numWpts&0xFF00)>>8);
1029 ret = sendVarPacket( (byte) PID_RECORDS, packs, (byte) 2 );
1030 if( ret != SUCCESS ) return ret;
1031
1032 if( numWpts > 500 ) numWpts = 500; // limit Garmin12
1033 for( i=0; i<numWpts; i++ ) {
1034 sendASingleWpt(i, (byte) PID_WPT_DATA, WPT_MODE );
1035 }
1036 }
1037
1038 // terminate wpt-transfer
1039 ret = sendVarPacket( (byte) PID_XFER_CMPLT, packs, (byte) 0 );
1040
1041 return ret;
1042 }
1043
1044 /**
1045 * Get the track (may consist of multiple segments) from the Gps.
1046 * Adds only trackpoints from the date specified.<br>
1047 * It also removes any segments that are considered not to be a track because
1048 * the distance moved is too small (tbd: add parameter!).<br>
1049 * This function is prepared to manage multiple
1050 * tracks for displaying / analysis (param: trackNumber).<br>
1051 * New: Supports A301 transfer protocol
1052 *
1053 * @param date<br>
1054 * 0 : any date (load complete track)<br>
1055 * any : specified date (time should be set to 00:00 in UTC[ms]; to be improved) <br>
1056 * @param trackNumber: number (buffer) of the track to use (ignored, for later enhancements)<br>
1057 * @param readMode: Gps.REPLACE or Gps.APPEND track
1058 *
1059 * @return SUCCESS <br>
1060 * BAD_CRC: CRC-error <br>
1061 * TX_RETRY_ERROR if too much NACKs have been received; <br>
1062 * IO_NOT_INITIALIZED
1063 *
1064 * @exception GpsTimeoutException ...
1065 * @exception IOException ...
1066 */
1067 public int getTrack( long date, int trackNumber, int mode ) throws GpsTimeoutException, IOException {
1068 int ret;
1069
1070 int lat,
1071 lon;
1072 int numPacks;
1073
1074 byte locBuf[] = new byte[2];
1075 int pidTmp = PID_TRK_DATA;
1076 Date date1 = new Date();
1077 boolean firstOne = true;
1078 float height;
1079
1080 if( !ioInit ) return IO_NOT_INITIALIZED;
1081 if( gpsId == 0 ) {
1082 // make sure Gps has been detetced:
1083 ret = getGpsId();
1084 if( ret != SUCCESS ) return ret;
1085 }
1086
1087 // send request to GPS: send wpts
1088 locBuf[0] = CMD_TRANSFER_TRK;
1089 ret = sendVarPacket( (byte) PID_CMD_DATA, locBuf, (byte) 2 );
1090 if((debug&0x01)!=0) printDebug("ret: " + ret + " ---");
1091 if( ret != SUCCESS ) return ret;
1092 // printDebug("(n)ack pid: " + pid + " ---");
1093 //if( pid != PID_ACK ) return 0;
1094
1095 // GPS sends PID_RECORDS and num packets to come:
1096 ret = getPacket(false);
1097 if( ret != SUCCESS ) return ret;
1098 if((debug&0x01)!=0) printDebug("data pid (expected: 27): " + pid + " ---");
1099 if((debug&0x01)!=0) printDebug("num packets: " + absByte(dataBuf[0]) + " " + absByte(dataBuf[1]));
1100 numPacks = absByte(dataBuf[0]) + (absByte(dataBuf[1])<<8);
1101 if((debug&0x01)!=0) printDebug("num packets to come: " + numPacks + " ---\nPlease wait ...");
1102 // confirm data:
1103 sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
1104
1105 // read track coordinates
1106 if( mode == Gps.REPLACE ) trkCont.removeTracksFromTrack(0);
1107 if( numPacks > 0 ) {
1108
1109 while( (pidTmp == PID_TRK_DATA) || (pidTmp == PID_TRK_HDR) ) {
1110 ret = getPacket(false);
1111 pidTmp = pid;
1112 if( ret == SUCCESS ) {
1113 // confirm data:
1114 sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
1115 }
1116 else {
1117 // doesn't work as expected sometimes :-(
1118 sendVarPacket( (byte) PID_NACK, msg, (byte) 0 ); // NACK
1119 continue;
1120 }
1121
1122 // ignore track header data
1123 if( (pidTmp != PID_XFER_CMPLT) && (pidTmp != PID_TRK_HDR) ) {
1124 Trk curTrk = new Trk();
1125 // setup input stream to read data from buffer:
1126 DataInputStream in =
1127 new DataInputStream(
1128 new BufferedInputStream(
1129 new ByteArrayInputStream(dataBuf)));
1130 curTrk = new Trk();
1131
1132 lat = in.readInt();
1133 lat = swap32(lat);
1134 lon = in.readInt();
1135 lon = swap32(lon);
1136 curTrk.timeStamp = swap32(in.readInt());
1137 // Convert Garmin-time to UTC [ms]:
1138 curTrk.timeStamp += DIFF_UTC_GPS_TIME;
1139 curTrk.timeStamp *= 1000;
1140
1141 if( GpsCaps.trk_a == 301 ) {
1142 // is of ETREX-type or Geko 201
1143 int accum = 0;
1144 accum = in.readInt();
1145 accum = swap32(accum);
1146 height = Float.intBitsToFloat(accum);
1147 if( height == 1.0e25 ) { // not supported by device or unknown
1148 curTrk.height = 0;
1149 } else {
1150 curTrk.height = (int) height;
1151 }
1152 // dummy-read -> drop depth-value:
1153 in.readFloat();
1154 }
1155
1156 curTrk.newTrkSeg = in.readByte();
1157 if( (curTrk.newTrkSeg == 1) || (firstOne == true) ) {
1158 firstOne = false;
1159 date1.setTime(curTrk.timeStamp);
1160 printMessage("New track starts at (UTC) " + date1.toString() );
1161 }
1162 curTrk.dlat = ((double)lat*180)/MAX_INT;
1163 curTrk.dlon = ((double)lon*180)/MAX_INT;
1164 curTrk.rlat = curTrk.dlat * Math.PI * 2 / 360;
1165 curTrk.rlon = curTrk.dlon * Math.PI * 2 / 360;
1166
1167 // check date of waypoint:
1168 if((debug&0x01)!=0) printDebug("UTC-timestamp [s]: " + curTrk.timeStamp);
1169 if( ((curTrk.timeStamp >= date) && (curTrk.timeStamp < (date+86400000))) || (date == 0 ) ) {
1170 trkCont.addTrk(0,curTrk);
1171 }
1172 curTrk.calcSignature();
1173 }
1174
1175 } // while( pidTmp == PID_TRK_DATA )
1176
1177 trkCont.removeNonFlights(0, 0.1);
1178 } else {
1179 if((debug&0x01)!=0) printDebug("no track on GPS ---");
1180 }
1181
1182 return ret;
1183 }
1184
1185
1186 /**
1187 * Sends one route to the GPS.
1188 * Route null is the active route.
1189 * If a waypoint isn't found in the list, remaining waypoints are not transmitted any more.
1190 * New: Transfer protocol 201 is supportted, fixed to "line mode"; experimental
1191 *
1192 * @param index index of the route to send in the List
1193 *
1194 * @return SUCCESS <br>
1195 * BAD_CRC: CRC-error <br>
1196 * TX_RETRY_ERROR if too much NACKs have been received; <br>
1197 * WRONG_INDEX_ERROR if route-index too high <br>
1198 * RTE_NOT_FOUND_ERROR if route-index doesn't exist <br>
1199 * WPT_NOT_FOUND_ERROR if waypoint in route is not in the waypoint list <br>
1200 * IO_NOT_INITIALIZED
1201 *
1202 * @exception GpsTimeoutException ...
1203 * @exception IOException ...
1204 */
1205 public int sendRte(int rteNr, int index) throws GpsTimeoutException, IOException {
1206 int ret;
1207 int i,j;
1208
1209 byte[] packs = new byte[2];
1210 byte[] locBuf2 = new byte[41];
1211 byte[] rteLnk = {0,3, /*class = direct, swapped word (!)*/
1212 0,0,
1213 0,0,0,0,
1214 -1,-1,-1,-1,
1215 -1,-1,-1,-1,
1216 -1,-1,-1,-1,
1217 0 // no ident
1218 };
1219 int size;
1220
1221 String rteWpt;
1222
1223 if( !ioInit ) return IO_NOT_INITIALIZED;
1224 if( index < 0 ) return WRONG_INDEX_ERROR;
1225 // if( GpsCaps.rte_a == 201 ) return PROTOCOL_NOT_SUPPORTED;
1226 if( gpsId == 0 ) {
1227 // make sure Gps has been detetced:
1228 ret = getGpsId();
1229 if( ret != SUCCESS ) return ret;
1230 } else {
1231 // send NACK; may help to recover from interrupted transmissions:
1232 //sendVarPacket( (byte) PID_NACK, msg, (byte) 0 );
1233 }
1234
1235 // check index:
1236 size = rteCont.size();
1237 if((debug&0x01)!=0) printDebug("--- send route " + index + " " + size + " ---");
1238 if(size == 0) return RTE_NOT_FOUND_ERROR;
1239
1240 // setup input stream to write data to buffer:
1241 ByteArrayOutputStream locBuf = new ByteArrayOutputStream(60);
1242 DataOutputStream out = new DataOutputStream( locBuf );
1243
1244 // select route in the list:
1245 curRte = rteCont.routeAt(index);
1246
1247 // send number of records to follow to the GPS
1248 if((debug&0x01)!=0) printDebug("route " + curRte.rteNumber + " " + curRte.comment);
1249 if( GpsCaps.rte_a == 201 ) {
1250 packs[0] = (byte)(2*curRte.wptName.size()); // data packet(s) to follows
1251 } else {
1252 packs[0] = (byte)(1+curRte.wptName.size()); // data packet(s) to follows
1253 }
1254 packs[1] = 0;
1255 ret = sendVarPacket( (byte) PID_RECORDS, packs, (byte) 2 );
1256 if( ret != SUCCESS ) return ret;
1257
1258 // create and send route header:
1259 if( GpsCaps.rte_d[0] == 200 ) {
1260 out.writeByte((byte) rteNr); // route number
1261 }
1262 else if( GpsCaps.rte_d[0] == 201 ) {
1263 out.writeByte((byte) rteNr); // route number
1264 curRte.comment = curRte.comment.toUpperCase();
1265 out.writeBytes(curRte.comment + " "); // append bytes to get 20 bytes
1266 }
1267 else if( GpsCaps.rte_d[0] == 202 ) {
1268 curRte.comment = curRte.comment.toUpperCase();
1269 out.writeBytes(curRte.comment);
1270 out.writeByte(0);
1271 }
1272
1273 locBuf2 = locBuf.toByteArray(); // get buffer
1274 // send header:
1275 if((debug&0x01)!=0) printDebug("route header ...");
1276 ret = sendVarPacket( (byte) PID_RTE_HDR, locBuf2, (byte) out.size() );
1277 if( ret != SUCCESS ) return ret;
1278
1279 // now search the waypoints in the list and send them to the GPS:
1280 size = curRte.wptName.size();
1281 for( i=0; i<size; i++) {
1282 // get name of next wpt:
1283 rteWpt = (String) curRte.wptName.elementAt(i);
1284 // search it in the list of all wpts:
1285 for( j=0; j<wptCont.size(); j++ ) {
1286 curWpt = wptCont.wptAt(j);
1287 if( curWpt.name.compareTo(rteWpt) == 0 ) break; // wpt found in list
1288 }
1289
1290 if( j == wptCont.size() ) {
1291 if((debug&0x01)!=0) printDebug("rte-wpt not found: " + rteWpt);
1292 ret = WPT_NOT_FOUND_ERROR;
1293 break; // fatal error
1294 }
1295
1296 if((debug&0x01)!=0) printDebug("rte-wpt found: " + curWpt.name);
1297 ret = sendASingleWpt( j, (byte)PID_RTE_WPT_DATA, RTE_MODE );
1298 if( ret != SUCCESS ) break;
1299 if( (GpsCaps.rte_a == 201) && (i != (size-1)) ) {
1300 sendVarPacket( (byte) PID_RTE_LNK, rteLnk, (byte) 20 );
1301 }
1302 }
1303
1304 // terminate wpt-transfer
1305 sendVarPacket( (byte) PID_XFER_CMPLT, packs, (byte) 0 );
1306
1307 return ret;
1308 }
1309
1310
1311 /**
1312 * Read all storted routes from the GPS and store it locally.
1313 * The waypoints that make up a specific route are read in. If they don't already exist,
1314 * they will be added to the list of existing waypoints. This is checked by the name.
1315 * Existing routes will be
1316 * The route's waypoints are defined by the list of names of waypoints only to avoid ambiguities.
1317 * New: Transfer protocl A201 is partly supported (link data will be ignored)
1318 *
1319 * @param mode: Gps.REPLACE or Gps.APPEND route(s) to the list
1320 *
1321 * @return SUCCESS <br>
1322 * BAD_CRC: CRC-error <br>
1323 * TX_RETRY_ERROR if too much NACKs have been received; <br>
1324 * IO_NOT_INITIALIZED
1325 *
1326 * @exception GpsTimeoutException ...
1327 * @exception IOException ...
1328 */
1329 public int getRte(int mode) throws GpsTimeoutException, IOException {
1330 int ret;
1331 int i;
1332 int numPacks;
1333 byte locBuf[] = new byte[2];
1334 int pidTmp = PID_RTE_HDR;
1335 boolean addRoute = false; // no route read so far
1336
1337 if( !ioInit ) return IO_NOT_INITIALIZED;
1338 if( gpsId == 0 ) {
1339 // make sure Gps has been detetced:
1340 ret = getGpsId();
1341 if( ret != SUCCESS ) return ret;
1342 } else {
1343 // send NACK; may help to recover from interrupted transmissions:
1344 sendVarPacket( (byte) PID_NACK, msg, (byte) 0 );
1345 }
1346
1347
1348
1349 if((debug&0x01)!=0) printDebug("--- get route ---");
1350 // send request to GPS: send route
1351 locBuf[0] = CMD_TRANSFER_RTE;
1352 ret = sendVarPacket( (byte) PID_CMD_DATA, locBuf, (byte) 2 );
1353 if((debug&0x01)!=0) printDebug("ret: " + ret + " ---");
1354 if( ret != SUCCESS ) return ret;
1355
1356 // GPS sends PID_RECORDS and num packets to come:
1357 ret = getPacket(false);
1358 if( ret != SUCCESS ) return ret;
1359 if((debug&0x01)!=0) {
1360 printDebug("data pid (expected pid: 27): " + pid + " ---");
1361 printDebug("num packets: " + absByte(dataBuf[0]) + " " + absByte(dataBuf[1]));
1362 }
1363 numPacks = absByte(dataBuf[0]) + (absByte(dataBuf[1])<<8);
1364 if((debug&0x01)!=0) printDebug("num packets to come: " + numPacks + " ---");
1365 // confirm data:
1366 sendVarPacket( (byte) PID_ACK, msg, (byte) 0 ); // ACK
1367
1368 // read wpts:
1369 pid