1 /********************************************************************
2 ** @source JEEPS command functions
3 **
4 ** @author Copyright (C) 1999 Alan Bleasby
5 ** @version 1.0
6 ** @modified Dec 28 1999 Alan Bleasby. First version
7 ** @modified Copyright (C) 2005, 2006 Robert Lipe
8 ** @modified Copyright (C) 2007 Achim Schumacher
9 ** @modified Copyright (C) 2010 Martin Buck
10 ** @@
11 **
12 ** This library is free software; you can redistribute it and/or
13 ** modify it under the terms of the GNU Library General Public
14 ** License as published by the Free Software Foundation; either
15 ** version 2 of the License, or (at your option) any later version.
16 **
17 ** This library is distributed in the hope that it will be useful,
18 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
19 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20 ** Library General Public License for more details.
21 **
22 ** You should have received a copy of the GNU Library General Public
23 ** License along with this library; if not, write to the
24 ** Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
25 ** Boston, MA 02110-1301,  USA.
26 ********************************************************************/
27 #include "garmin_gps.h"
28 #include <stdio.h>
29 #include <float.h>
30 
31 #ifdef USE_WX_LOGGING
32 #include "gps_wx_logging.h"
33 #endif
34 
35 
36 /* @func GPS_Command_Off ***********************************************
37 **
38 ** Turn off power on GPS
39 **
40 ** @return [int32] success
41 ************************************************************************/
42 
GPS_Command_Off(const char * port)43 int32 GPS_Command_Off(const char *port)
44 {
45     static UC data[2];
46     gpsdevh *fd;
47     GPS_PPacket tra;
48     GPS_PPacket rec;
49 
50     GPS_Util_Little();
51 
52     if(!GPS_Device_On(port, &fd))
53 	return gps_errno;
54 
55     if(!(tra = GPS_Packet_New()) || !(rec = GPS_Packet_New()))
56 	return MEMORY_ERROR;
57 
58     GPS_Util_Put_Short(data,COMMAND_ID[gps_device_command].Cmnd_Turn_Off_Pwr);
59 
60     /* robertl - LINK_ID isn't set yet.  Hardcode it to Garmin spec value */
61     GPS_Make_Packet(&tra, 10, /* LINK_ID[gps_link_type].Pid_Command_Data, */
62 		    data,2);
63     if(!GPS_Write_Packet(fd,tra))
64 	return gps_errno;
65 
66     if(!GPS_Device_Chars_Ready(fd))
67     {
68 	if(!GPS_Get_Ack(fd, &tra, &rec))
69 	    return gps_errno;
70 	GPS_User("Power off command acknowledged");
71     }
72 
73     GPS_Packet_Del(&tra);
74     GPS_Packet_Del(&rec);
75 
76     if(!GPS_Device_Off(fd))
77 	return gps_errno;
78 
79     return 1;
80 }
81 
82 
83 /* @func GPS_Command_Get_Waypoint ***************************************
84 **
85 ** Get waypoint from GPS
86 **
87 ** @param [r] port [const char *] serial port
88 ** @param [w] way [GPS_PWay **] pointer to waypoint array
89 **
90 ** @return [int32] number of waypoint entries
91 ************************************************************************/
92 
GPS_Command_Get_Waypoint(const char * port,GPS_PWay ** way,pcb_fn cb)93 int32 GPS_Command_Get_Waypoint(const char *port, GPS_PWay **way, pcb_fn cb)
94 {
95     int32 ret=0;
96 
97     /*
98      * It's a bit tacky to do this up front without ticking the
99      * progress meter, but this come in pretty quickly...
100      */
101     if (gps_category_transfer) {
102 	ret = GPS_A101_Get(port);
103 	if (!ret) {
104 fatal("blah");
105 	   return PROTOCOL_ERROR;
106 	}
107 
108     }
109 
110     switch(gps_waypt_transfer)
111     {
112     case pA100:
113 	ret = GPS_A100_Get(port,way, cb);
114 	break;
115     default:
116 	GPS_Error("Get_Waypoint: Unknown waypoint protocol");
117 	return PROTOCOL_ERROR;
118     }
119 
120     return ret;
121 }
122 
123 
124 
125 /* @func GPS_Command_Send_Waypoint ******************************************
126 **
127 ** Send waypoints to GPS
128 **
129 ** @param [r] port [const char *] serial port
130 ** @param [r] way [GPS_PWay *] waypoint array
131 ** @param [r] n [int32] number of waypoint entries
132 **
133 ** @return [int32] success
134 ************************************************************************/
135 
GPS_Command_Send_Waypoint(const char * port,GPS_PWay * way,int32 n,int (* cb)(struct GPS_SWay **))136 int32 GPS_Command_Send_Waypoint(const char *port, GPS_PWay *way, int32 n, int (*cb)(struct GPS_SWay **))
137 {
138     int32 ret=0;
139 
140     switch(gps_waypt_transfer)
141     {
142     case pA100:
143 	ret = GPS_A100_Send(port, way, n, cb);
144 	break;
145     default:
146 	GPS_Error("Send_Waypoint: Unknown waypoint protocol");
147 	return PROTOCOL_ERROR;
148     }
149 
150     return ret;
151 }
152 
153 
154 /* @func GPS_Command_Get_Route **************************************
155 **
156 ** Get Route(s) from GPS
157 **
158 ** @param [r] port [const char *] serial port
159 ** @param [w] way [GPS_PWay **] pointer to waypoint array
160 **
161 ** @return [int32] number of waypoint entries
162 ************************************************************************/
163 
GPS_Command_Get_Route(const char * port,GPS_PWay ** way)164 int32 GPS_Command_Get_Route(const char *port, GPS_PWay **way)
165 {
166     int32 ret=0;
167 
168     switch(gps_route_transfer)
169     {
170     case pA200:
171 	ret = GPS_A200_Get(port,way);
172 	break;
173     case pA201:
174 	ret = GPS_A201_Get(port,way);
175 	break;
176     default:
177 	GPS_Error("Get_Route: Unknown route protocol");
178 	return PROTOCOL_ERROR;
179     }
180 
181     return ret;
182 }
183 
184 
185 
186 /* @func GPS_Command_Send_Route ****************************************
187 **
188 ** Send route(s) to GPS
189 **
190 ** @param [r] port [const char *] serial port
191 ** @param [r] way [GPS_PWay *] waypoint array
192 ** @param [r] n [int32] number of waypoint entries
193 **
194 ** @return [int32] success
195 ************************************************************************/
196 
GPS_Command_Send_Route(const char * port,GPS_PWay * way,int32 n)197 int32 GPS_Command_Send_Route(const char *port, GPS_PWay *way, int32 n)
198 {
199     int32 ret=0;
200 
201 
202     switch(gps_route_transfer)
203     {
204     case pA200:
205 	ret = GPS_A200_Send(port, way, n);
206 	break;
207     case pA201:
208 	ret = GPS_A201_Send(port, way, n);
209 	break;
210     default:
211 	GPS_Error("Send_Route: Unknown route protocol");
212 	return PROTOCOL_ERROR;
213     }
214 
215     return ret;
216 }
217 
218 
219 /* @func GPS_Command_Get_Track ***************************************
220 **
221 ** Get track log from GPS
222 **
223 ** @param [r] port [const char *] serial port
224 ** @param [w] trk [GPS_PTrack **] pointer to track array
225 **
226 ** @return [int32] number of track entries
227 ************************************************************************/
228 
GPS_Command_Get_Track(const char * port,GPS_PTrack ** trk,pcb_fn cb)229 int32 GPS_Command_Get_Track(const char *port, GPS_PTrack **trk, pcb_fn cb)
230 {
231     int32 ret=0;
232 
233     if(gps_trk_transfer == -1)
234 	return GPS_UNSUPPORTED;
235 
236     switch(gps_trk_transfer)
237     {
238     case pA300:
239 	ret = GPS_A300_Get(port,trk,cb);
240 	break;
241     case pA301:
242     case pA302:
243 	ret = GPS_A301_Get(port,trk,cb,301);
244 	break;
245     default:
246 	GPS_Error("Get_Track: Unknown track protocol %d\n", gps_trk_transfer);
247 	return PROTOCOL_ERROR;
248     }
249 
250     return ret;
251 }
252 
253 
254 
255 /* @func GPS_Command_Send_Track ******************************************
256 **
257 ** Send track log to GPS
258 **
259 ** @param [r] port [const char *] serial port
260 ** @param [r] trk [GPS_PTrack *] track array
261 ** @param [r] n [int32] number of track entries
262 **
263 ** @return [int32] success
264 ************************************************************************/
265 
GPS_Command_Send_Track(const char * port,GPS_PTrack * trk,int32 n)266 int32 GPS_Command_Send_Track(const char *port, GPS_PTrack *trk, int32 n)
267 {
268     int32 ret=0;
269 
270     if(gps_trk_transfer == -1)
271 	return GPS_UNSUPPORTED;
272 
273     switch(gps_trk_transfer)
274     {
275     case pA300:
276 	ret = GPS_A300_Send(port, trk, n);
277 	break;
278     case pA301:
279 	ret = GPS_A301_Send(port, trk, n, 301, NULL);
280 	break;
281     case pA302:
282        /* Units with A302 don't support track upload, so we convert the
283         * track to a course on the fly and send that instead
284         */
285 	ret = GPS_Command_Send_Track_As_Course(port, trk, n, NULL, 0);
286        break;
287     default:
288 	GPS_Error("Send_Track: Unknown track protocol %d.", gps_trk_transfer);
289 	break;
290     }
291 
292     return ret;
293 }
294 
295 
296 /* @func GPS_Command_Get_Proximity **************************************
297 **
298 ** Get proximitywaypoint from GPS
299 **
300 ** @param [r] port [const char *] serial port
301 ** @param [w] way [GPS_PWay **] pointer to waypoint array
302 **
303 ** @return [int32] number of waypoint entries
304 ************************************************************************/
305 
GPS_Command_Get_Proximity(const char * port,GPS_PWay ** way)306 int32 GPS_Command_Get_Proximity(const char *port, GPS_PWay **way)
307 {
308     int32 ret=0;
309 
310     if(gps_prx_waypt_transfer == -1)
311 	return GPS_UNSUPPORTED;
312 
313     switch(gps_prx_waypt_transfer)
314     {
315     case pA400:
316 	ret = GPS_A400_Get(port,way);
317 	break;
318     default:
319 	GPS_Error("Get_Proximity: Unknown proximity protocol");
320 	return PROTOCOL_ERROR;
321     }
322 
323     return ret;
324 }
325 
326 
327 
328 /* @func GPS_Command_Send_Proximity ******************************************
329 **
330 ** Send proximity waypoints to GPS
331 **
332 ** @param [r] port [const char *] serial port
333 ** @param [r] way [GPS_PWay *] waypoint array
334 ** @param [r] n [int32] number of waypoint entries
335 **
336 ** @return [int32] success
337 ************************************************************************/
338 
GPS_Command_Send_Proximity(const char * port,GPS_PWay * way,int32 n)339 int32 GPS_Command_Send_Proximity(const char *port, GPS_PWay *way, int32 n)
340 {
341     int32 ret=0;
342 
343 
344     if(gps_prx_waypt_transfer == -1)
345 	return GPS_UNSUPPORTED;
346 
347 
348     switch(gps_prx_waypt_transfer)
349     {
350     case pA400:
351 	ret = GPS_A400_Send(port, way, n);
352 	break;
353     default:
354 	GPS_Error("Send_Proximity: Unknown proximity protocol");
355 	break;
356     }
357 
358     return ret;
359 }
360 
361 
362 
363 /* @func GPS_Command_Get_Almanac ***************************************
364 **
365 ** Get almanac from GPS
366 **
367 ** @param [r] port [const char *] serial port
368 ** @param [w] alm [GPS_PAlmanac **] pointer to almanac array
369 **
370 ** @return [int32] number of almanac entries
371 ************************************************************************/
372 
GPS_Command_Get_Almanac(const char * port,GPS_PAlmanac ** alm)373 int32 GPS_Command_Get_Almanac(const char *port, GPS_PAlmanac **alm)
374 {
375     int32 ret=0;
376 
377     if(gps_almanac_transfer == -1)
378 	return GPS_UNSUPPORTED;
379 
380     switch(gps_almanac_transfer)
381     {
382     case pA500:
383 	ret = GPS_A500_Get(port,alm);
384 	break;
385     default:
386 	GPS_Error("Get_Almanac: Unknown almanac protocol");
387 	return PROTOCOL_ERROR;
388     }
389 
390     return ret;
391 }
392 
393 
394 
395 /* @func GPS_Command_Send_Almanac ******************************************
396 **
397 ** Send almanac to GPS
398 **
399 ** @param [r] port [const char *] serial port
400 ** @param [r] alm [GPS_PAlmanac *] almanac array
401 ** @param [r] n [int32] number of almanac entries
402 **
403 ** @return [int32] success
404 ************************************************************************/
405 
GPS_Command_Send_Almanac(const char * port,GPS_PAlmanac * alm,int32 n)406 int32 GPS_Command_Send_Almanac(const char *port, GPS_PAlmanac *alm, int32 n)
407 {
408     int32 ret=0;
409 
410 	if(gps_almanac_transfer == -1)
411 		return GPS_UNSUPPORTED;
412 
413     switch(gps_almanac_transfer)
414     {
415     case pA500:
416 	ret = GPS_A500_Send(port, alm, n);
417 	break;
418     default:
419 	GPS_Error("Send_Almanac: Unknown almanac protocol");
420 	break;
421     }
422 
423     return ret;
424 }
425 
426 
427 
428 /* @func GPS_Command_Get_Time ******************************************
429 **
430 ** Get time from GPS
431 **
432 ** @param [r] port [const char *] serial port
433 **
434 ** @return [time_t] unix-style time
435 ************************************************************************/
436 
GPS_Command_Get_Time(const char * port)437 time_t GPS_Command_Get_Time(const char *port)
438 {
439     time_t ret=0;
440 
441     switch(gps_date_time_transfer)
442     {
443     case pA600:
444 	ret = GPS_A600_Get(port);
445 	break;
446     /*
447      * If the unit doesn't support it (i.e. a C320 in charging mode),
448      * but don't treat as error; return as zero.
449      */
450     case -1:
451 	return 0;
452     default:
453 	GPS_Error("Get_Time: Unknown date/time protocol");
454 	return PROTOCOL_ERROR;
455     }
456 
457     return ret;
458 }
459 
460 
461 
462 /* @func GPS_Command_Send_Time ******************************************
463 **
464 ** Set GPS time
465 **
466 ** @param [r] port [const char *] serial port
467 ** @param [r] Time [time_t] unix-style time
468 **
469 ** @return [int32] true if OK
470 ************************************************************************/
471 
GPS_Command_Send_Time(const char * port,time_t Time)472 int32 GPS_Command_Send_Time(const char *port, time_t Time)
473 {
474     time_t ret=0;
475 
476     switch(gps_date_time_transfer)
477     {
478     case pA600:
479 	ret = GPS_A600_Send(port, Time);
480 	break;
481     default:
482 	GPS_Error("Send_Time: Unknown date/time protocol");
483 	return PROTOCOL_ERROR;
484     }
485 
486     return (int32)ret;
487 }
488 
489 
490 
491 
492 /* @func GPS_Command_Get_Position ***************************************
493 **
494 ** Get position from GPS
495 **
496 ** @param [r] port [const char *] serial port
497 ** @param [w] lat [double *] latitude  (deg)
498 ** @param [w] lon [double *] longitude (deg)
499 **
500 ** @return [int32] success
501 ************************************************************************/
502 
GPS_Command_Get_Position(const char * port,double * lat,double * lon)503 int32 GPS_Command_Get_Position(const char *port, double *lat, double *lon)
504 {
505     int32 ret=0;
506 
507     switch(gps_position_transfer)
508     {
509     case pA700:
510 	ret = GPS_A700_Get(port,lat,lon);
511 	break;
512     /*
513      * If the unit doesn't support it (i.e. a C320 in charging mode),
514      *  zero lat/lon, but don't treat as error.
515      */
516     case -1:
517 	*lat = *lon = 0.0;
518 	break;
519     default:
520 	GPS_Error("Get_Position: Unknown position protocol");
521 	return PROTOCOL_ERROR;
522     }
523 
524     return ret;
525 }
526 
527 
528 
529 /* @func GPS_Command_Send_Position ******************************************
530 **
531 ** Set GPS position
532 **
533 ** @param [r] port [const char *] serial port
534 ** @param [r] lat [double] latitude  (deg)
535 ** @param [r] lon [double] longitude (deg)
536 **
537 ** @return [int32] success
538 ************************************************************************/
539 
GPS_Command_Send_Position(const char * port,double lat,double lon)540 int32 GPS_Command_Send_Position(const char *port, double lat, double lon)
541 {
542     int32 ret=0;
543 
544     switch(gps_position_transfer)
545     {
546     case pA700:
547 	ret = GPS_A700_Send(port, lat, lon);
548 	break;
549     default:
550 	GPS_Error("Send_Position: Unknown position protocol");
551 	return PROTOCOL_ERROR;
552     }
553 
554     return ret;
555 }
556 
557 
558 /* @func GPS_Command_Pvt_On ********************************************
559 **
560 ** Instruct GPS to start sending Pvt data every second
561 **
562 ** @param [r] port [const char *] serial port
563 ** @param [w] fd [int32 *] file descriptor
564 **
565 ** @return [int32] success if supported and GPS starts sending
566 ************************************************************************/
567 
GPS_Command_Pvt_On(const char * port,gpsdevh ** fd)568 int32 GPS_Command_Pvt_On(const char *port, gpsdevh **fd)
569 {
570     int32 ret=0;
571 
572 
573     if(gps_pvt_transfer == -1)
574 	return GPS_UNSUPPORTED;
575 
576 
577     switch(gps_pvt_transfer)
578     {
579     case pA800:
580 	ret = GPS_A800_On(port,fd);
581 	break;
582     default:
583 	GPS_Error("Pvt_On: Unknown position protocol");
584 	return PROTOCOL_ERROR;
585     }
586 
587 
588     return ret;
589 }
590 
591 
592 
593 /* @func GPS_Command_Pvt_Off ********************************************
594 **
595 ** Instruct GPS to stop sending Pvt data every second
596 **
597 ** @param [r] port [const char *] serial port
598 ** @param [w] fd [int32 *] file descriptor
599 **
600 ** @return [int32] success
601 ************************************************************************/
602 
GPS_Command_Pvt_Off(const char * port,gpsdevh ** fd)603 int32 GPS_Command_Pvt_Off(const char *port, gpsdevh **fd)
604 {
605     int32 ret=0;
606 
607 
608     if(gps_pvt_transfer == -1)
609 	return GPS_UNSUPPORTED;
610 
611     switch(gps_pvt_transfer)
612     {
613     case pA800:
614 	ret = GPS_A800_Off(port,fd);
615 	break;
616     default:
617 	GPS_Error("Pvt_Off: Unknown position protocol");
618 	return PROTOCOL_ERROR;
619     }
620 
621     return ret;
622 }
623 
624 
625 
626 /* @func GPS_Command_Pvt_Get ********************************************
627 **
628 ** Get a single PVT info entry
629 **
630 ** @param [w] fd [int32 *] file descriptor
631 ** @param [w] pvt [GPS_PPvt_Data *] pvt data structure to fill
632 **
633 ** @return [int32] success
634 ************************************************************************/
635 
GPS_Command_Pvt_Get(gpsdevh ** fd,GPS_PPvt_Data * pvt)636 int32 GPS_Command_Pvt_Get(gpsdevh **fd, GPS_PPvt_Data *pvt)
637 {
638     int32 ret=0;
639 
640     if(gps_pvt_transfer == -1)
641 	return GPS_UNSUPPORTED;
642 
643     (*pvt)->fix = 0;
644 
645     switch(gps_pvt_transfer)
646     {
647     case pA800:
648 	ret = GPS_A800_Get(fd,pvt);
649 	break;
650     default:
651 	GPS_Error("Pvt_Get: Unknown position protocol");
652 	return PROTOCOL_ERROR;
653     }
654 
655     return ret;
656 }
657 
658 /* @func GPS_Command_Get_Lap ***************************************
659 **
660 ** Get lap from GPS
661 **
662 ** @param [r] port [const char *] serial port
663 ** @param [w] lap [GPS_PLap **] pointer to lap array
664 **
665 ** @return [int32] number of lap entries
666 ************************************************************************/
667 
GPS_Command_Get_Lap(const char * port,GPS_PLap ** lap,pcb_fn cb)668 int32 GPS_Command_Get_Lap(const char *port, GPS_PLap **lap, pcb_fn cb)
669 {
670     int32 ret=0;
671 
672     if(gps_lap_transfer == -1)
673 	return GPS_UNSUPPORTED;
674 
675     switch(gps_lap_transfer)
676     {
677 	case pA906:
678 	    ret = GPS_A906_Get(port,lap, cb);
679 	    break;
680 	default:
681 	    GPS_Error("Get_Lap: Unknown lap protocol");
682 	    return PROTOCOL_ERROR;
683     }
684 
685     return ret;
686 }
687 
688 /* @func GPS_Command_Get_Course ***************************************
689 **
690 ** Get Courses from GPS. According to Garmin protocol specification, this
691 ** includes getting all course laps, course tracks and course points
692 ** from the device.
693 **
694 ** @param [r] port [const char *] serial port
695 ** @param [w] crs [GPS_PCourse **] pointer to course array
696 ** @param [w] clp [GPS_PCourse_Lap **] pointer to course lap array
697 ** @param [w] trk [GPS_PTrack **] pointer to track array
698 ** @param [w] cpt [GPS_PCourse_Point **] pointer to course point array
699 ** @param [w] n_clp [int32 **] pointer to number of lap entries
700 ** @param [w] n_trk [int32 **] pointer to number of track entries
701 ** @param [w] n_cpt [int32 **] pointer to number of course point entries
702 **
703 ** @return [int32] number of course entries
704 ************************************************************************/
GPS_Command_Get_Course(const char * port,GPS_PCourse ** crs,GPS_PCourse_Lap ** clp,GPS_PTrack ** trk,GPS_PCourse_Point ** cpt,int32 * n_clp,int32 * n_trk,int32 * n_cpt,pcb_fn cb)705 int32  GPS_Command_Get_Course
706                 (const char *port,
707                  GPS_PCourse **crs,
708                  GPS_PCourse_Lap **clp,
709                  GPS_PTrack **trk,
710                  GPS_PCourse_Point **cpt,
711                  int32 *n_clp,
712                  int32 *n_trk,
713                  int32 *n_cpt,
714                  pcb_fn cb)
715 {
716     int32 ret=0;
717 
718     if(gps_course_transfer == -1)
719        return GPS_UNSUPPORTED;
720 
721     switch(gps_course_transfer)
722     {
723        case pA1006:
724            ret = GPS_A1006_Get(port,crs,cb);
725            break;
726        default:
727            GPS_Error("Get_Course: Unknown course protocol");
728            return PROTOCOL_ERROR;
729     }
730 
731     switch(gps_course_lap_transfer)
732     {
733        case pA1007:
734            *n_clp = GPS_A1007_Get(port,clp, 0);
735            break;
736        default:
737            GPS_Error("Get_Course: Unknown course lap protocol");
738            return PROTOCOL_ERROR;
739     }
740 
741     switch(gps_course_trk_transfer)
742     {
743         case pA1012:
744            GPS_Error("Get_Course: Not implemented track protocol %d\n",
745                             gps_trk_transfer);
746            break;
747         case pA302:
748 	    *n_trk = GPS_A301_Get(port,trk,cb,302);
749            break;
750         default:
751            GPS_Error("Get_Course: Unknown course track protocol %d\n",
752                             gps_trk_transfer);
753            return PROTOCOL_ERROR;
754     }
755 
756     switch(gps_course_point_transfer)
757     {
758        case pA1008:
759            *n_cpt = GPS_A1008_Get(port,cpt, 0);
760            break;
761        default:
762            GPS_Error("Get_Course: Unknown course point protocol");
763            return PROTOCOL_ERROR;
764     }
765 
766     return ret;
767 }
768 
769 
770 /* @func GPS_Command_Send_Course ***************************************
771 **
772 ** Send Courses to GPS. According to Garmin protocol specification, this
773 ** includes sending all course laps, course tracks and course points
774 ** to the device.
775 **
776 ** @param [r] port [const char *] serial port
777 ** @param [w] crs [GPS_PCourse **] course array
778 ** @param [w] clp [GPS_PCourse_Lap *] course lap array
779 ** @param [w] trk [GPS_PTrack *] track array
780 ** @param [w] cpt [GPS_PCourse_Point *] course point array
781 ** @param [w] n_crs [int32] number of course entries
782 ** @param [w] n_clp [int32] number of lap entries
783 ** @param [w] n_trk [int32] number of track entries
784 ** @param [w] n_cpt [int32] number of course point entries
785 **
786 ** @return [int32] Success
787 ************************************************************************/
GPS_Command_Send_Course(const char * port,GPS_PCourse * crs,GPS_PCourse_Lap * clp,GPS_PTrack * trk,GPS_PCourse_Point * cpt,int32 n_crs,int32 n_clp,int32 n_trk,int32 n_cpt)788 int32  GPS_Command_Send_Course
789                 (const char *port,
790                  GPS_PCourse *crs,
791                  GPS_PCourse_Lap *clp,
792                  GPS_PTrack *trk,
793                  GPS_PCourse_Point *cpt,
794                  int32 n_crs,
795                  int32 n_clp,
796                  int32 n_trk,
797                  int32 n_cpt)
798 {
799     gpsdevh *fd;
800     GPS_OCourse_Limits limits;
801     int32 ret;
802     int32 ret_crs=0;
803     int32 ret_clp=0;
804     int32 ret_trk=0;
805     int32 ret_cpt=0;
806 
807     if(gps_course_transfer == -1 || gps_course_limits_transfer == -1)
808        return GPS_UNSUPPORTED;
809 
810     /* Check course limits to make sure we're not exceeding the device's
811      * capacity.
812      */
813     switch(gps_course_limits_transfer)
814     {
815        case pA1009:
816            ret = GPS_A1009_Get(port,&limits);
817            break;
818        default:
819            GPS_Error("Send_Course: Unknown course limitsprotocol");
820            return PROTOCOL_ERROR;
821     }
822 /*
823     if (n_crs > limits.max_courses
824         || n_clp > limits.max_course_laps
825         || n_trk > limits.max_course_trk_pnt
826         || n_cpt > limits.max_course_pnt)
827     {
828 	GPS_Error("Course upload would exceed device capacity:");
829 	GPS_Error("# of courses: %d, max: %d", n_crs, limits.max_courses);
830 	GPS_Error("# of laps: %d, max: %d", n_clp, limits.max_course_laps);
831 	GPS_Error("# of track points: %d, max: %d", n_trk, limits.max_course_trk_pnt);
832 	GPS_Error("# of course points: %d, max: %d", n_cpt, limits.max_course_pnt);
833 	return GPS_UNSUPPORTED;
834     }
835 */
836     /* Initialize device communication:
837      * In contrast to other transfer protocols, this has to be handled here;
838      * shutting off communication in between the different parts
839      * could lead to data corruption on the device because all the courses
840      * and their associated lap and track data have to be sent in one
841      * transaction.
842      */
843     if(!GPS_Device_On(port,&fd))
844         return gps_errno;
845 
846     switch(gps_course_transfer)
847     {
848        case pA1006:
849            ret_crs = GPS_A1006_Send(port,crs,n_crs,fd);
850            break;
851        default:
852            GPS_Error("Send_Course: Unknown course protocol");
853            return PROTOCOL_ERROR;
854     }
855 
856     switch(gps_course_lap_transfer)
857     {
858        case pA1007:
859            ret_clp = GPS_A1007_Send(port,clp,n_clp,fd);
860            break;
861        default:
862            GPS_Error("Send_Course: Unknown course lap protocol");
863            return PROTOCOL_ERROR;
864     }
865 
866     switch(gps_course_trk_transfer)
867     {
868         case pA1012:
869            GPS_Error("Send_Course: Not implemented track protocol %d\n",
870                             gps_trk_transfer);
871            break;
872         case pA302:
873 	    ret_trk = GPS_A301_Send(port,trk,n_trk,302,fd);
874            break;
875         default:
876            GPS_Error("Send_Course: Unknown course track protocol %d\n",
877                             gps_trk_transfer);
878            return PROTOCOL_ERROR;
879     }
880 
881     switch(gps_course_point_transfer)
882     {
883        case pA1008:
884            ret_cpt = GPS_A1008_Send(port,cpt,n_cpt,fd);
885            break;
886        default:
887            GPS_Error("Send_Course: Unknown course point protocol");
888            return PROTOCOL_ERROR;
889     }
890 
891     if(!GPS_Device_Off(fd))
892         return gps_errno;
893 
894 
895     return ret_crs * ret_clp * ret_trk * ret_cpt;
896 }
897 
898 
899 /* @funcstatic Unique_Course_Index *************************************
900 **
901 ** Choose a course index that's not yet used by another course.
902 **
903 ** @param [r] crs [GPS_PCourse **] course array
904 ** @param [r] n_crs [int32] number of course entries
905 **
906 ** @return [uint32] course index
907 ************************************************************************/
Unique_Course_Index(GPS_PCourse * crs,int n_crs)908 uint32 Unique_Course_Index(GPS_PCourse *crs, int n_crs)
909 {
910     uint32 idx;
911     int i;
912 
913     for (idx=0; ; idx++)
914     {
915         for (i=0; i<n_crs; i++)
916             if (crs[i]->index==idx)
917                 break; /* Already have this index */
918         if (i>=n_crs)
919             return idx; /* Found unused index */
920     }
921 }
922 
923 
924 /* @funcstatic Unique_Track_Index ***************************************
925 **
926 ** Choose a track index that's not yet used by another track referenced
927 ** by the courses.
928 **
929 ** @param [r] crs [GPS_PCourse **] course array
930 ** @param [r] n_crs [int32] number of course entries
931 **
932 ** @return [uint32] track index
933 ************************************************************************/
Unique_Track_Index(GPS_PCourse * crs,int n_crs)934 uint32 Unique_Track_Index(GPS_PCourse *crs, int n_crs)
935 {
936     uint32 idx;
937     int i;
938 
939     for (idx=0; ; idx++)
940     {
941         for (i=0; i<n_crs; i++)
942             if (crs[i]->track_index==idx)
943                 break; /* Already have this index */
944         if (i>=n_crs)
945             return idx; /* Found unused index */
946     }
947 }
948 
949 
950 /* @funcstatic Calculate_Course_Lap_Data *******************************
951 **
952 ** Calculate lap data totals from individual track points. Also
953 ** generates time stamps for track points if they don't have
954 ** time stamps yet (using an arbitrary speed of 10 km/h which is
955 ** currently hardcoded. This is required so that couse points can
956 ** refer to track points and identify them uniquely.
957 **
958 ** @param [w] clp [GPS_PCourse_Lap] course lap to be calculated
959 ** @param [r] ctk [GPS_PTrack *] track array to calculate lap from
960 ** @param [r] ctk_start [int] start index of lap in track array
961 ** @param [r] ctk_end [int] end index of lap in track array
962 **
963 ** @return [void]
964 ************************************************************************/
965 void
Calculate_Course_Lap_Data(GPS_PCourse_Lap clp,GPS_PTrack * ctk,int ctk_start,int ctk_end)966 Calculate_Course_Lap_Data(GPS_PCourse_Lap clp, GPS_PTrack *ctk,
967                           int ctk_start, int ctk_end)
968 {
969 #if 0  //dsr
970     int i;
971     double heartrate_sum = 0, cadence_sum = 0;
972     int heartrate_sum_time = 0, cadence_sum_time = 0;
973     double time_synth_speed = 10.0 * 1000 / 3600; /* speed in m/s */
974 
975     if (ctk_start && ctk_end && !ctk[ctk_start]->Time)
976 	ctk[ctk_start]->Time = GPS_Time_Now();
977     else
978 	time_synth_speed = 0;
979 
980     clp->total_dist = 0;
981     clp->avg_heart_rate = 0;
982     clp->max_heart_rate = 0;
983     clp->intensity = 0;
984     clp->avg_cadence = 0xff;
985     for (i=ctk_start; i <= ctk_end; i++)
986     {
987 	if (ctk[i]->heartrate && ctk[i]->heartrate > clp->max_heart_rate)
988 	    clp->max_heart_rate = ctk[i]->heartrate;
989 	if (i < ctk_end)
990 	{
991 	    double dist = 0;
992 	    int seg_time;
993 
994 	    if (!ctk[i]->no_latlon && !ctk[i+1]->no_latlon)
995 		dist = gcgeodist(ctk[i]->lat, ctk[i]->lon,
996 		                 ctk[i+1]->lat, ctk[i+1]->lon);
997 	    clp->total_dist += dist;
998 
999 	    if (time_synth_speed)
1000 		ctk[i+1]->Time = ctk[i]->Time + (dist / time_synth_speed + 0.5);
1001 
1002 	    seg_time = ctk[i+1]->Time - ctk[i]->Time;
1003 
1004 	    if (ctk[i]->heartrate)
1005 	    {
1006 		heartrate_sum += ctk[i]->heartrate * seg_time;
1007 		heartrate_sum_time += seg_time;
1008 	    }
1009 	    if (ctk[i]->cadence)
1010 	    {
1011 		cadence_sum += ctk[i]->cadence * seg_time;
1012 		cadence_sum_time += seg_time;
1013 	    }
1014 	}
1015     }
1016 
1017     clp->total_time = 0;
1018     clp->begin_lat = 0x7fffffff;
1019     clp->begin_lon = 0x7fffffff;
1020     clp->end_lat = 0x7fffffff;
1021     clp->end_lon = 0x7fffffff;
1022     if (ctk_start && ctk_end)
1023     {
1024 	clp->total_time = (ctk[ctk_end]->Time - ctk[ctk_start]->Time) * 100;
1025 	if (!ctk[ctk_start]->no_latlon && !ctk[ctk_end]->no_latlon)
1026 	{
1027 	    clp->begin_lat = ctk[ctk_start]->lat;
1028 	    clp->begin_lon = ctk[ctk_start]->lon;
1029 	    clp->end_lat = ctk[ctk_end]->lat;
1030 	    clp->end_lon = ctk[ctk_end]->lon;
1031 	}
1032     }
1033     if (heartrate_sum_time)
1034 	clp->avg_heart_rate = heartrate_sum / heartrate_sum_time;
1035     if (cadence_sum_time)
1036 	clp->avg_cadence = cadence_sum / cadence_sum_time;
1037 #endif
1038 }
1039 
1040 
1041 /* @funcstatic Course_Garbage_Collect **********************************
1042 **
1043 ** Remove duplicate courses, then remove unreferenced laps, tracks and
1044 ** course points from arrays.
1045 **
1046 ** @param [w] crs [GPS_PCourse *] course array
1047 ** @param [w] n_crs [int *] number of course entries
1048 ** @param [w] clp [GPS_PCourse_Lap *] course lap array
1049 ** @param [w] n_clp [int *] number of lap entries
1050 ** @param [w] ctk [GPS_PTrack *] track array
1051 ** @param [w] n_ctk [int *] number of track entries
1052 ** @param [w] cpt [GPS_PCourse_Point *] course point array
1053 ** @param [w] n_cpt [int *] number of course point entries
1054 **
1055 ** @return [void]
1056 ************************************************************************/
1057 void
Course_Garbage_Collect(GPS_PCourse * crs,int * n_crs,GPS_PCourse_Lap * clp,int * n_clp,GPS_PTrack * ctk,int * n_ctk,GPS_PCourse_Point * cpt,int * n_cpt)1058 Course_Garbage_Collect(GPS_PCourse *crs, int *n_crs,
1059                        GPS_PCourse_Lap *clp, int *n_clp,
1060                        GPS_PTrack *ctk, int *n_ctk,
1061                        GPS_PCourse_Point *cpt, int *n_cpt)
1062 {
1063     int i, j;
1064 
1065     /* Remove courses with duplicate names, keeping newest.
1066      * This is actually pretty important: Sending two courses with the same
1067      * name to the device will result in internal data corruption on the
1068      * device (e.g. "inventing" laps with weird course IDs that nobody ever
1069      * transferred to it, that change with every upload of unrelated data
1070      * and that can't be deleted except with a master reset by holding the
1071      * Mode button during power on).
1072      */
1073 restart_courses:
1074     for (i=*n_crs-1; i>0; i--)
1075     {
1076         for (j=i-1; j>=0; j--)
1077         {
1078             if (!strcmp(crs[i]->course_name, crs[j]->course_name))
1079             {
1080                 /* Remove course */
1081                 GPS_Course_Del(&crs[j]);
1082                 memmove(&crs[j], &crs[j+1], (*n_crs-j-1)*sizeof(*crs));
1083                 (*n_crs)--;
1084                 goto restart_courses;
1085             }
1086         }
1087     }
1088 
1089   /* Remove unreferenced laps */
1090 restart_laps:
1091     for (i=0; i<*n_clp; i++)
1092     {
1093         for (j=0; j<*n_crs; j++)
1094             if (crs[j]->index == clp[i]->course_index)
1095                 break;
1096         if (j>=*n_crs)
1097         {
1098             /* Remove lap */
1099             GPS_Course_Lap_Del(&clp[i]);
1100             memmove(&clp[i], &clp[i+1], (*n_clp-i-1)*sizeof(*clp));
1101             (*n_clp)--;
1102             goto restart_laps;
1103         }
1104     }
1105 
1106     /* Remove unreferenced tracks */
1107 restart_tracks:
1108     for (i=0; i<*n_ctk; i++)
1109     {
1110         uint32 trk_idx;
1111 
1112         if (!ctk[i]->ishdr)
1113             continue;
1114         trk_idx = strtoul(ctk[i]->trk_ident, NULL, 0);
1115         for (j=0; j<*n_crs; j++)
1116             if (crs[j]->track_index == trk_idx)
1117                 break;
1118         if (j>=*n_crs)
1119         {
1120             /* Remove track */
1121             for (j=i; j<*n_ctk; j++)
1122             {
1123                 if (j!=i && ctk[j]->ishdr)
1124                     break;
1125                 GPS_Track_Del(&ctk[j]);
1126             }
1127             memmove(&ctk[i], &ctk[j], (*n_ctk-j)*sizeof(*ctk));
1128             *(n_ctk) -= j-i;
1129             goto restart_tracks;
1130         }
1131     }
1132 
1133     /* Remove unreferenced/duplicate course points */
1134 restart_course_points:
1135     for (i=0; i<*n_cpt; i++)
1136     {
1137         /* Check for unreferenced point */
1138         for (j=0; j<*n_crs; j++)
1139             if (crs[j]->index == cpt[i]->course_index)
1140                 break;
1141         if (j<*n_crs)
1142         {
1143             /* Check for duplicate point */
1144             for (j=0; j<i; j++)
1145                 if (cpt[i]->course_index == cpt[j]->course_index &&
1146                     cpt[i]->track_point_time == cpt[j]->track_point_time)
1147                     break;
1148             if (j>=i)
1149                 continue; /* Referenced & unique */
1150         }
1151         /* Remove course point */
1152         GPS_Course_Point_Del(&cpt[i]);
1153         memmove(&cpt[i], &cpt[i+1], (*n_cpt-i-1)*sizeof(*cpt));
1154         (*n_cpt)--;
1155         goto restart_course_points;
1156     }
1157 }
1158 
1159 
1160 /* @func GPS_Command_Send_Track_As_Course ******************************
1161 **
1162 ** Convert track log to course, then send to GPS. Since sending a course
1163 ** to the device will erase all existing courses regardless of their
1164 ** name or index, we first have to download all courses, merge the new
1165 ** one and then send all courses at once.
1166 **
1167 ** @param [r] port [const char *] serial port
1168 ** @param [r] trk [GPS_PTrack *] track array
1169 ** @param [r] n_trk [int32] number of track entries
1170 ** @param [r] wpt [GPS_PWay *] waypoint array
1171 ** @param [r] n_wpt [int32] number of waypoint entries
1172 **
1173 ** @return [int32] success
1174 ************************************************************************/
1175 
GPS_Command_Send_Track_As_Course(const char * port,GPS_PTrack * trk,int32 n_trk,GPS_PWay * wpt,int32 n_wpt)1176 int32 GPS_Command_Send_Track_As_Course(const char *port, GPS_PTrack *trk, int32 n_trk,
1177                                        GPS_PWay *wpt, int32 n_wpt)
1178 {
1179 #if 0 //dsr
1180     GPS_PCourse *crs = NULL;
1181     GPS_PCourse_Lap *clp = NULL;
1182     GPS_PTrack *ctk = NULL;
1183     GPS_PCourse_Point *cpt = NULL;
1184     int n_crs, n_clp=0, n_ctk=0, n_cpt=0;
1185     int i, j, trk_end, new_crs, first_new_ctk;
1186     int32 ret;
1187 
1188     /* Read existing courses from device */
1189     n_crs = GPS_Command_Get_Course(port, &crs, &clp, &ctk, &cpt, &n_clp, &n_ctk, &n_cpt, NULL);
1190     if (n_crs < 0) return n_crs;
1191 
1192     /* Create new course+lap+track points for each track */
1193     new_crs = n_crs;
1194     for (i=0;i<n_trk;i++) {
1195         if (!trk[i]->ishdr)
1196             continue;
1197 
1198         /* Find end of track */
1199 	for (trk_end=i; trk_end<n_trk-1; trk_end++)
1200             if (trk[trk_end+1]->ishdr)
1201                 break;
1202         if (trk_end==i)
1203             continue; /* Skip empty track */
1204 
1205         /* Create & append course */
1206         crs = xrealloc(crs, (n_crs+1) * sizeof(GPS_PCourse));
1207         crs[n_crs] = GPS_Course_New();
1208         if (!crs[n_crs]) return MEMORY_ERROR;
1209 
1210         crs[n_crs]->index = Unique_Course_Index(crs, n_crs);
1211         strncpy(crs[n_crs]->course_name, trk[i]->trk_ident,
1212                 sizeof(crs[n_crs]->course_name)-1);
1213 
1214         crs[n_crs]->track_index = Unique_Track_Index(crs, n_crs);
1215 
1216         /* Create & append new lap */
1217         clp = xrealloc(clp, (n_clp+1) * sizeof(GPS_PCourse_Lap));
1218         clp[n_clp] = GPS_Course_Lap_New();
1219         if (!clp[n_clp]) return MEMORY_ERROR;
1220 
1221         clp[n_clp]->course_index = crs[n_crs]->index; /* Index of associated course */
1222         clp[n_clp]->lap_index = 0; /* Lap index, unique per course */
1223 	Calculate_Course_Lap_Data(clp[n_clp], trk, i+1, trk_end);
1224         n_crs++;
1225         n_clp++;
1226     }
1227 
1228     /* Append new track points */
1229     ctk = xrealloc(ctk, (n_ctk+n_trk) * sizeof(GPS_PTrack));
1230     first_new_ctk = n_ctk;
1231     for (i=0;i<n_trk;i++) {
1232         if (trk[i]->ishdr && (i>=n_trk || trk[i+1]->ishdr))
1233             continue;
1234 
1235         ctk[n_ctk] = GPS_Track_New();
1236         if (!ctk[n_ctk]) return MEMORY_ERROR;
1237         *ctk[n_ctk] = *trk[i];
1238 
1239         if (trk[i]->ishdr)
1240         {
1241             /* Index of new track, must match the track index in associated course */
1242             memset(ctk[n_ctk]->trk_ident, 0, sizeof(ctk[n_ctk]->trk_ident));
1243             sprintf(ctk[n_ctk]->trk_ident, "%d", crs[new_crs]->track_index);
1244             new_crs++;
1245         }
1246         n_ctk++;
1247     }
1248 
1249     /* Convert waypoints to course points by searching closest track point &
1250      * append
1251      */
1252     cpt = xrealloc(cpt, (n_cpt+n_wpt) * sizeof(GPS_PCourse_Point));
1253     for (i=0; i<n_wpt; i++)
1254     {
1255 	double dist, min_dist = DBL_MAX;
1256 	int min_dist_idx = 0, trk_idx = 0, min_dist_trk_idx = 0;
1257 
1258 	/* Find closest track point */
1259 	for (j=first_new_ctk; j<n_ctk; j++) {
1260 	    if (ctk[j]->ishdr) {
1261 		trk_idx = strtoul(ctk[j]->trk_ident, NULL, 0);
1262 		continue;
1263 	    }
1264 
1265 	    dist = gcgeodist(wpt[i]->lat, wpt[i]->lon, ctk[j]->lat, ctk[j]->lon);
1266 	    if (dist < min_dist) {
1267 		min_dist = dist;
1268 		min_dist_idx = j;
1269 		min_dist_trk_idx = trk_idx;
1270 	    }
1271 	}
1272 
1273 	cpt[i+n_cpt] = GPS_Course_Point_New();
1274 	strncpy(cpt[i+n_cpt]->name, wpt[i]->cmnt,
1275 	        sizeof(cpt[i+n_cpt]->name) - 1);
1276 	for (j=0; j<n_crs; j++)
1277 	    if (crs[j]->track_index == min_dist_trk_idx)
1278 	    {
1279 		cpt[i+n_cpt]->course_index = crs[j]->index;
1280 		break;
1281 	    }
1282 	cpt[i+n_cpt]->track_point_time = ctk[min_dist_idx]->Time;
1283 	cpt[i+n_cpt]->point_type = 0;
1284     }
1285     n_cpt += n_wpt;
1286 
1287     /* Remove course data that's no longer needed */
1288     Course_Garbage_Collect(crs, &n_crs, clp, &n_clp, ctk, &n_ctk, cpt, &n_cpt);
1289 
1290     /* Finally send courses including new ones to device */
1291     ret = GPS_Command_Send_Course(port, crs, clp, ctk, cpt,
1292                                   n_crs, n_clp, n_ctk, n_cpt);
1293 
1294     for (i=0;i<n_crs;i++)
1295     {
1296        GPS_Course_Del(&crs[i]);
1297     }
1298     free(crs);
1299 
1300     for (i=0;i<n_clp;i++)
1301     {
1302        GPS_Course_Lap_Del(&clp[i]);
1303     }
1304     free(clp);
1305 
1306     for (i=0;i<n_ctk;i++)
1307     {
1308        GPS_Track_Del(&ctk[i]);
1309     }
1310     free(ctk);
1311 
1312     for (i=0;i<n_cpt;i++)
1313     {
1314        GPS_Course_Point_Del(&cpt[i]);
1315     }
1316     free(cpt);
1317 
1318     return ret;
1319 #endif
1320     return 0;
1321 }
1322 
1323  /*Stubs for unimplemented stuff*/
GPS_Command_Get_Workout(const char * port,void ** lap,int (* cb)(int,struct GPS_SWay **))1324 int32  GPS_Command_Get_Workout(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **)){
1325   return 0;
1326 }
1327 
GPS_Command_Get_Fitness_User_Profile(const char * port,void ** lap,int (* cb)(int,struct GPS_SWay **))1328 int32  GPS_Command_Get_Fitness_User_Profile(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **)){
1329   return 0;
1330 }
1331 
GPS_Command_Get_Workout_Limits(const char * port,void ** lap,int (* cb)(int,struct GPS_SWay **))1332 int32  GPS_Command_Get_Workout_Limits(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **)){
1333   return 0;
1334 }
1335 
GPS_Command_Get_Course_Limits(const char * port,void ** lap,int (* cb)(int,struct GPS_SWay **))1336 int32  GPS_Command_Get_Course_Limits(const char *port, void **lap, int (*cb)(int, struct GPS_SWay **)){
1337   return 0;
1338 }
1339