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