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