1
// -*- mode: c++; c-basic-offset: 8; -*-
2
// $Id: garmin_application.cpp,v 1.4 2003/04/28 15:42:38 ganter Exp $
3
// garmin_application.cp
4
// Douglas S. J. De Couto
7
// Copyright (C) 1998 Douglas S. J. De Couto
8
// <decouto@lcs.mit.edu>
10
// This program is free software; you can redistribute it and/or
11
// modify it under the terms of the GNU General Public License
12
// as published by the Free Software Foundation; either version 2
13
// of the License, or (at your option) any later version.
15
// This program is distributed in the hope that it will be useful,
16
// but WITHOUT ANY WARRANTY; without even the implied warranty of
17
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
// GNU General Public License for more details.
21
#include "garmin_application.h"
22
#include "garmin_command.h"
23
#include "garmin_util.h"
24
#include "garmin_data.h"
25
#include "garmin_legacy.h"
38
#define check_init() if (!m_init) throw not_possible("The garmin application layer is not initialized")
42
// Aborts any pending transfer
45
application_layer::abort_transfer(void) throw (timeout)
50
sint16 *buf16 = (sint16 *) m_buf;
53
buf16[0] = host2garmin16((sint16) cmnd_abort_transfer);
54
m_ll->put_packet(pid_command_data, m_buf, 2);
61
application_layer::get_product_data(void) throw (not_possible, timeout)
69
// First, abort any pending transfer:
72
m_ll->put_packet(pid_product_rqst, m_buf, 0);
73
pid = m_ll->get_packet(m_buf, sz);
75
if (pid != pid_product_data) {
76
throw not_possible("GPS did not reply with product data packet");
80
throw not_possible("Product data packet is too small");
84
sint16 *buf16 = (sint16 *) m_buf;
85
pd.product_id = garmin2host16(buf16[0]);
86
pd.software_version = garmin2host16(buf16[1]);
87
pd.product_description = &((char *) m_buf)[4];
89
// get capability protocol info... (A001),
90
// but not supported on all units!
92
pid = m_ll->get_packet(m_buf, sz);
95
// timeout indicates gps is not sending capability protocol info
96
m_got_protocol_info = false;
98
// we should compose our own capability info from the device id
99
for (int i = 0; legacy_devices[i].id; i++)
101
legacy_device *ldp = &legacy_devices[i];
103
if ( ldp->id == pd.product_id &&
104
ldp->version_min <= pd.software_version &&
105
ldp->version_max >= pd.software_version)
107
// found it, creating capability info
108
for (int j = 0; ldp->protocols[j].tag ; j++)
110
protocol_datatypes_t x;
111
for (int k = 0; ldp->protocols[j].types[k] != type_end; k++)
113
x.push_back(ldp->protocols[j].types[k]);
115
pd.protocol_info[(ldp->protocols[j].id << 8) | ((unsigned char)ldp->protocols[j].tag)] = x;
124
m_got_protocol_info = true;
125
if (pid != pid_protocol_array) {
126
throw not_possible("Product data packet followed by some packet besides capability protocol");
129
throw not_possible("Capability protocol packet is strange size");
133
// we store now the protocols support by the device and the
134
// associated data types
137
// contains the last found protocol id
138
// used to known where to store the datatype infos
140
for (sint16 i = 0; i < sz; i +=3) {
141
char tag = (char) m_buf[i];
142
uint16 data = garmin2host16(*(uint16 *) (m_buf + i + 1));
146
// is a protocol info
147
protocol_datatypes_t x;
148
lastdata = (data << 8) | ((unsigned char)tag);
149
pd.protocol_info[lastdata] = x;
153
// is a protocol data type
154
assert(lastdata != -1);
155
pd.protocol_info[lastdata].push_back(data);
165
// This reads the display bitmap on GPS models that support this.
166
// Saves the bitmap into a BMP file
169
// Screen data is stored as unsigned long format, each long (32 bits)
170
// contains 16 pixels, with 2 bits per pixel.
172
// 0= white, 1=Light grey, 2=dark grey, 3=black
174
// Please note that display is actually mirrored! We would need to put everything
175
// into a table in memory before flushing it to disk if we wanted to have the right
179
application_layer::get_display_bitmap(void) throw (not_possible, timeout)
182
product_data_type pd = get_product_data();
185
uint16 width, height;
186
sint16 *buf16 = (sint16 *) m_buf;
189
// I'm using the BMP format because it's the one used by g7to and I don't
190
// have enough time to create PNG or something fancy like this.
191
// This is a standard BMP header adapted to our purpose:
192
unsigned char bmh[118] = {
193
66, 77, 182, 31, 0, 0, 0, 0, 0, 0, 118, 0, 0,
194
0, 40, 0, 0, 0, 160, 0, 0, 0, 100, 0, 0, 0,
195
1, 0, 4, 0, 0, 0, 0, 0, 64, 31, 0, 0, 0,
196
0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 16, 0,
197
0, 0, 0, 0, 0, 0, 99, 99, 99, 0, 181, 181, 181,
198
0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0,
199
255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255,
200
255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255,
201
255, 0, 255, 255, 255, 0, 255, 255, 255, 0, 255, 255, 255, 0
204
// Abort any pending/failed transfer:
207
buf16[0] = host2garmin16((sint16) cmnd_transfer_screenbitmap);
208
m_ll->put_packet(pid_command_data, m_buf, 2);
210
packet_id pid = m_ll->get_packet(m_buf, sz);
212
if (pid != pid_display_data) {
213
throw not_possible("GPS did not reply with start of screen bitmap data packet");
216
// First packet contains screen width and lenght at the following offsets:
218
// 20: height (number of lines)
219
width = *(uint16 *) (m_buf + 16);
220
height =*(uint16 *) (m_buf + 20);
221
cout << "Screen size: " << width << "x" << height << endl;
223
// Open the screen.bmp file and write the header
226
outfile = fopen("screenshot.bmp","w");
227
tlong=width*height/2;
228
memcpy(&bmh[34],&tlong,4);
230
memcpy(&bmh[2],&tlong,4);
231
fwrite(bmh,118,1,outfile);
233
// Then parse each subsequent packet and
234
// write the data into the BMP file after parsing.
236
for (int i=0; i< height; i++) {
237
// Get next packet with screen line
238
pid = m_ll->get_packet(m_buf, sz);
239
if (pid != pid_display_data) throw not_possible("GPS did not reply with screen bitmap data packet");
241
ptr = *(uint32*) (m_buf+q);
242
// Offset 4 is the pixel number starting the line
243
pixnum = *(uint32*) (m_buf + 4);
244
cout << "Line number " << i << "\r" << flush;
245
// Add the pixels to the new pixel bitmap:
250
for (int j=0; j< width / 16; j++) {
253
for(int j=1;j<5;j++) { // for each byte in long
254
for(int k=1;k<3;k++) { // for each pixel in the byte
256
// Our bmp output file has one nibble per pixel
257
// first nibble of byte
259
ptr = *(uint32*)(m_buf+q);
260
a = (~(uint8)(((ptr) & mask) >> bit_shift)) & 3;
264
// second nibble of byte
266
b= (~(uint8)(((ptr)&mask)>>bit_shift))&3;
270
// Write line into file:
273
fwrite(&c,1,1,outfile);
276
q +=4; // index pointer to next 4 bytes (unsigned long)
285
application_layer::get_pvt(void) throw (not_possible, timeout)
289
sint16 *sbuf16 = (sint16 *) m_buf;
291
sbuf16[0] = host2garmin16((sint16) cmnd_start_pvt_data);
292
m_ll->put_packet(pid_command_data, m_buf, 2);
295
packet_id pid = m_ll->get_packet(m_buf, sz);
297
if (pid != pid_pvt_data) {
298
throw not_possible("GPS did not respond with position packet");
301
throw not_possible("Position packet is the wrong size");
304
double64 *dbuf = (double64 *) m_buf;
306
retval.lat = garmin2host64(dbuf[0]);
307
retval.lon = garmin2host64(dbuf[1]);
319
application_layer::get_waypoints(void) throw (not_possible, timeout, unsupported_protocol)
323
product_data_type pd = get_product_data();
325
if (!pd.protocol_info.empty() && !pd.has_protocol('A',100))
327
throw unsupported_protocol("GPS does not support selected protocol");
331
sint16 *buf16 = (sint16 *) m_buf;
334
ident[6] = cmnt[40] = 0;
336
buf16[0] = host2garmin16((sint16) cmnd_transfer_wpt);
337
m_ll->put_packet(pid_command_data, m_buf, 2);
339
packet_id pid = m_ll->get_packet(m_buf, sz);
341
if (pid != pid_records) {
342
throw not_possible("GPS did not reply with start of records data packet for waypoint data");
345
throw not_possible("Waypoint data start of records packet is wrong size");
348
sint16 num_recs = garmin2host16(buf16[0]);
349
waypt_vec_t *waypts = new waypt_vec_t(num_recs);
352
for (sint16 i = 0; i < num_recs; i++) {
353
basic_waypt_type &waypt = (*waypts)[i];
355
pid = m_ll->get_packet(m_buf, sz);
357
if (pid != pid_wpt_data) {
359
throw not_possible("GPS did not send waypoint data packet");
362
if (sz < (6 + 8 + 4 + 40)) {
364
throw not_possible("Waypoint data packet is too small");
367
switch (pd.protocol_datatype('A',100,0))
379
uint32 *buf32 = (uint32 *) (m_buf + 6);
380
waypt.pos.lat = garmin2host32(buf32[0]);
381
waypt.pos.lon = garmin2host32(buf32[1]);
383
strncpy(ident, (char *) m_buf, 6);
384
waypt.id = string(ident);
386
strncpy(cmnt, (char *) (m_buf + 18), 40);
387
waypt.comment = string(cmnt);
391
unsupp = true; // Cannot just bail out, otherwise the GPS is confused
394
if (unsupp) throw unsupported_protocol("Waypoint Datatype of GPS not supported");
396
pid = m_ll->get_packet(m_buf, sz);
397
if (pid != pid_xfer_cmplt) {
399
throw not_possible("GPS did not terminate waypoint data with end of records packet");
401
if (garmin2host16(buf16[0]) != cmnd_transfer_wpt) {
403
throw not_possible("End of records packet does not match original waypoint transfer command");
410
/////////////////////
413
application_layer::send_waypoints(waypt_vec_t *waypts) throw (not_possible, timeout)
417
const int d103_sz = 6 + 8 + 4 + 40 + 2;
419
product_data_type pd = get_product_data();
421
if (!(pd.has_protocol('A',100) && pd.protocol_datatype('A',100,0) == 103))
423
throw not_possible("Waypoint download not supported for this GPS");
426
sint16 *buf16 = (sint16 *) m_buf;
427
buf16[0] = host2garmin16(waypts->size());
429
m_ll->put_packet(pid_records, m_buf, 2);
431
for (unsigned int i = 0; i < waypts->size(); i++) {
432
basic_waypt_type &w = (*waypts)[i];
435
memset(m_buf, 0, d103_sz);
436
// set all waypt strings to blank spaces
437
memset(m_buf, ' ', 6);
438
memset(m_buf + 18, ' ', 40);
440
// copy in strings, fixing up for GPS; leave bad characters as spaces
441
for (size_t j = 0; j < min<size_t>(6, w.id.size()); j++) {
444
m_buf[j] = toupper(c);
447
for (size_t j = 0; j < min<size_t>(40, w.comment.length()); j++) {
448
char c = w.comment[j];
449
if (isalnum(c) || c == '-') {
450
m_buf[j + 18] = toupper(c);
454
// copy in position data
455
uint32 *buf32 = (uint32 *) (m_buf + 6);
456
buf32[0] = host2garmin32(w.pos.lat);
457
buf32[1] = host2garmin32(w.pos.lon);
459
m_ll->put_packet(pid_wpt_data, m_buf, d103_sz);
462
buf16[0] = host2garmin16(cmnd_transfer_wpt);
463
m_ll->put_packet(pid_xfer_cmplt, m_buf, 2);
466
/////////////////////
469
application_layer::get_routes() throw (not_possible, timeout)
472
pd = get_product_data();
474
if (!pd.protocol_info.empty() && pd.has_protocol('A',201))
476
return get_routes_201();
479
if (!pd.protocol_info.empty() && pd.has_protocol('A',200))
481
return get_routes_200();
484
return get_routes_200();
491
application_layer::get_routes_201() throw (not_possible, timeout, unsupported_protocol)
495
if (!pd.protocol_info.empty() && !pd.has_protocol('A',201))
497
throw unsupported_protocol("GPS does not support selected protocol");
501
uint16 *buf16 = (uint16 *) m_buf;
502
buf16[0] = host2garmin16((sint16) cmnd_transfer_rte);
503
m_ll->put_packet(pid_command_data, m_buf, 2);
506
packet_id pid = m_ll->get_packet(m_buf, sz);
507
if (pid != pid_records) {
508
throw not_possible("GPS did not reply with start of records data packet for route data");
511
throw not_possible("Route data start of records packet is wrong size");
513
sint16 num_packets = garmin2host16(buf16[0]);
515
// create route list with no routes
516
route_list_t *retval = new route_list_t(0);
518
bool curr_rt_exists = false;
519
for (sint16 i = 0; i < num_packets; i++) {
520
pid = m_ll->get_packet(m_buf, sz);
521
if (pid == pid_rte_hdr) {
523
retval->push_back(list<basic_waypt_type>());
524
curr_rt_exists = true;
527
if (pid == pid_rte_link_data) {
528
curr_rt_exists = true;
532
if (pid != pid_rte_wpt_data) {
534
throw not_possible("Packet is not route waypoint data or route header");
536
if (!curr_rt_exists) {
538
throw not_possible("Route waypoint data packet was not preceded by a route header packet");
540
if (sz < (6 + 8 + 2 + 40)) {
542
throw not_possible("Route waypoint data packet is too small");
545
basic_waypt_type waypt;
550
// add this waypoint to end of last route in route list.
551
retval->back().push_back(waypt);
555
pid = m_ll->get_packet(m_buf, sz);
556
if (pid != pid_xfer_cmplt) {
558
throw not_possible("GPS did not terminate route data with end of records packet");
560
if (garmin2host16(buf16[0]) != cmnd_transfer_rte) {
562
throw not_possible("End of records packet does not match original route transfer command");
568
/////////////////////
571
application_layer::get_routes_200(void) throw (not_possible, timeout, unsupported_protocol)
575
product_data_type pd = get_product_data();
577
if (!pd.protocol_info.empty() && !pd.has_protocol('A',200))
579
throw unsupported_protocol("GPS does not support selected protocol");
582
uint16 *buf16 = (uint16 *) m_buf;
583
buf16[0] = host2garmin16((sint16) cmnd_transfer_rte);
584
m_ll->put_packet(pid_command_data, m_buf, 2);
587
packet_id pid = m_ll->get_packet(m_buf, sz);
588
if (pid != pid_records) {
589
throw not_possible("GPS did not reply with start of records data packet for route data");
592
throw not_possible("Route data start of records packet is wrong size");
594
sint16 num_packets = garmin2host16(buf16[0]);
596
// create route list with no routes
597
route_list_t *retval = new route_list_t(0);
599
bool curr_rt_exists = false;
600
for (sint16 i = 0; i < num_packets; i++) {
601
pid = m_ll->get_packet(m_buf, sz);
602
if (pid == pid_rte_hdr) {
604
retval->push_back(list<basic_waypt_type>());
605
curr_rt_exists = true;
609
if (pid != pid_rte_wpt_data) {
611
throw not_possible("Packet is not route waypoint data or route header");
613
if (!curr_rt_exists) {
615
throw not_possible("Route waypoint data packet was not preceded by a route header packet");
617
if (sz < (6 + 8 + 2 + 40)) {
619
throw not_possible("Route waypoint data packet is too small");
622
semicircle_type *sp = (semicircle_type *) (m_buf + 6);
623
sp->lat = garmin2host32(sp->lat);
624
sp->lon = garmin2host32(sp->lon);
626
basic_waypt_type waypt;
628
char ident[7], cmnt[41];
629
ident[6] = cmnt[40] = 0;
630
strncpy(ident, (char *) m_buf, 6);
631
waypt.id = string(ident);
635
strncpy(cmnt, (char *) m_buf + 14, 40);
636
waypt.comment = string(cmnt);
638
// add this waypoint to end of last route in route list.
639
retval->back().push_back(waypt);
643
pid = m_ll->get_packet(m_buf, sz);
644
if (pid != pid_xfer_cmplt) {
646
throw not_possible("GPS did not terminate route data with end of records packet");
648
if (garmin2host16(buf16[0]) != cmnd_transfer_rte) {
650
throw not_possible("End of records packet does not match original route transfer command");
657
/////////////////////
660
application_layer::send_routes(route_list_t *routes) throw (not_possible, timeout)
664
product_data_type pd = get_product_data();
666
if (!(pd.has_protocol('A',200) && pd.protocol_datatype('A',200,1) == 103))
668
throw not_possible("Waypoint download not supported for this GPS");
671
const int d103_sz = 6 + 8 + 4 + 40 + 2;
673
// how many data packets?
675
route_list_t::const_iterator llw;
676
for (llw = routes->begin(); llw != routes->end(); llw++) {
677
num_recs += llw->size();
679
num_recs += routes->size(); // plus 1 header packet for each route
681
sint16 *buf16 = (sint16 *) m_buf;
682
buf16[0] = host2garmin16(num_recs);
684
m_ll->put_packet(pid_records, m_buf, 2);
686
for (llw = routes->begin(); llw != routes->end(); llw++) {
690
char *test = "TESTING COMMENT ";
691
for (int k = 0; k < 20; k++) {
692
m_buf[k + 1] = test[k];
694
m_ll->put_packet(pid_rte_hdr, m_buf, 21);
696
// put route waypoints
697
route_t::const_iterator lw;
698
for (lw = llw->begin(); lw != llw->end(); lw++) {
699
memset(m_buf, 0, d103_sz);
700
// set all waypt strings to blank spaces
701
memset(m_buf, ' ', 6);
702
memset(m_buf + 18, ' ', 40);
704
// copy in strings, fixing up for GPS; leave bad characters as spaces
705
for (size_t j = 0; j < min<size_t>(6, lw->id.size()); j++) {
708
m_buf[j] = toupper(c);
711
for (size_t j = 0; j < min<size_t>(40, lw->comment.length()); j++) {
712
char c = lw->comment[j];
713
if (isalnum(c) || c == '-') {
714
m_buf[j + 18] = toupper(c);
718
// copy in position data
719
uint32 *buf32 = (uint32 *) (m_buf + 6);
720
buf32[0] = host2garmin32(lw->pos.lat);
721
buf32[1] = host2garmin32(lw->pos.lon);
723
m_ll->put_packet(pid_rte_wpt_data, m_buf, d103_sz);
726
buf16[0] = host2garmin16(cmnd_transfer_rte);
727
m_ll->put_packet(pid_xfer_cmplt, m_buf, 2);
730
/////////////////////
733
application_layer::send_track_logs(track_list_t *tracks) throw (not_possible, timeout)
737
// how many track points?
739
track_list_t::const_iterator llti;
740
for (llti = tracks->begin(); llti != tracks->end(); llti++) {
741
num_recs += llti->size();
744
sint16 *sbuf16 = (sint16 *) m_buf;
745
sbuf16[0] = host2garmin16(num_recs);
747
m_ll->put_packet(pid_records, m_buf, 2);
749
for (llti = tracks->begin(); llti != tracks->end(); llti++) {
750
track_t::const_iterator lti;
753
for (lti = llti->begin(); lti != llti->end(); lti++, first = false) {
755
uint32 *buf32 = (uint32 *) m_buf;
756
buf32[0] = host2garmin32(lti->pos.lat);
757
buf32[1] = host2garmin32(lti->pos.lon);
759
// GPS ignores time field, leave it as 0
761
m_ll->put_packet(pid_trk_data, m_buf, 16);
763
// set new_track element at first point of each segment, but not for other
764
// points in track segment
771
sbuf16[0] = host2garmin16(cmnd_transfer_trk);
772
m_ll->put_packet(pid_xfer_cmplt, m_buf, 2);
775
/////////////////////
778
application_layer::get_track_logs(void) throw (not_possible, timeout, unsupported_protocol)
781
product_data_type pd = get_product_data();
783
if (!pd.protocol_info.empty() && pd.has_protocol('A',301))
785
return get_track_logs_301();
788
if (!pd.protocol_info.empty() && pd.has_protocol('A',300))
790
return get_track_logs_300();
793
return get_track_logs_300();
798
application_layer::get_track_logs_300(void) throw (not_possible, timeout, unsupported_protocol)
802
product_data_type pd = get_product_data();
804
if (!pd.protocol_info.empty() && !pd.has_protocol('A',300))
806
throw unsupported_protocol("GPS does not support selected protocol");
810
sint16 *buf16 = (sint16 *) m_buf;
812
buf16[0] = host2garmin16((sint16) cmnd_transfer_trk);
813
m_ll->put_packet(pid_command_data, m_buf, 2);
815
packet_id pid = m_ll->get_packet(m_buf, sz);
817
if (pid != pid_records) {
818
throw not_possible("GPS did not reply with start of records data packet for track log data");
821
throw not_possible("Track log data start of records packet is wrong size");
824
sint16 num_recs = garmin2host16(buf16[0]);
825
track_list_t *tracks = new track_list_t(0);
827
for (sint16 i = 0; i < num_recs; i++) {
828
pid = m_ll->get_packet(m_buf, sz);
830
if (pid != pid_trk_data) {
832
throw not_possible("GPS did not send track point data packet");
835
if (sz < (8 + 4 + 1)) {
837
throw not_possible("Track point data packet is too small");
840
bool new_trk = m_buf[8 + 4];
841
if (new_trk || i == 0) {
842
tracks->push_back(list<track_point_type>());
845
semicircle_type *sp = (semicircle_type *) m_buf;
846
sp->lat = garmin2host32(sp->lat);
847
sp->lon = garmin2host32(sp->lon);
849
track_point_type track_point;
850
track_point.pos = *sp;
851
track_point.new_track = new_trk;
852
uint32 *buf32 = (uint32 *) m_buf;
855
memset(&my_tm, 0, sizeof(struct tm));
856
my_tm.tm_sec = garmin2host32(buf32[2]);
858
my_tm.tm_mday = 31; // 1 based counting
859
my_tm.tm_mon = 11; // 0 based counting
860
my_tm.tm_isdst = -1; // unknown
862
track_point.time = mktime(&my_tm);
864
tracks->back().push_back(track_point);
867
pid = m_ll->get_packet(m_buf, sz);
868
if (pid != pid_xfer_cmplt) {
870
throw not_possible("GPS did not terminate track log data with end of records packet");
872
if (garmin2host16(buf16[0]) != cmnd_transfer_trk) {
874
throw not_possible("End of records packet does not match original track log transfer command");
881
application_layer::get_track_logs_301(void) throw (not_possible, timeout, unsupported_protocol)
885
product_data_type pd = get_product_data();
887
if (!pd.protocol_info.empty() && !pd.has_protocol('A',301))
889
throw unsupported_protocol("GPS does not support selected protocol");
893
sint16 *buf16 = (sint16 *) m_buf;
895
buf16[0] = host2garmin16((sint16) cmnd_transfer_trk);
896
m_ll->put_packet(pid_command_data, m_buf, 2);
898
packet_id pid = m_ll->get_packet(m_buf, sz);
900
if (pid != pid_records) {
901
throw not_possible("GPS did not reply with start of records data packet for track log data");
904
throw not_possible("Track log data start of records packet is wrong size");
907
sint16 num_recs = garmin2host16(buf16[0]);
908
track_list_t *tracks = new track_list_t(0);
911
for (sint16 i = 0; i < num_recs; i++) {
912
pid = m_ll->get_packet(m_buf, sz);
915
if (pid == pid_trk_hdr) {
916
cout << "New track: " << m_buf + 2 << endl;
920
if (pid != pid_trk_data) {
922
throw not_possible("GPS did not send track point data packet");
925
if (sz < (8 + 4 + 1)) {
927
throw not_possible("Track point data packet is too small");
930
bool new_trk = m_buf[8 + 4 + 8];
931
if (new_trk || i == 0) {
932
tracks->push_back(list<track_point_type>());
935
track_point_type track_point;
938
track_point << my_d301;
939
tracks->back().push_back(track_point);
942
pid = m_ll->get_packet(m_buf, sz);
943
if (pid != pid_xfer_cmplt) {
945
throw not_possible("GPS did not terminate track log data with end of records packet");
947
if (garmin2host16(buf16[0]) != cmnd_transfer_trk) {
949
throw not_possible("End of records packet does not match original track log transfer command");
958
application_layer::get_proximity_waypoints(void) throw (not_possible, timeout, unsupported_protocol)
962
product_data_type pd = get_product_data();
964
if (!pd.protocol_info.empty() && !pd.has_protocol('A',400))
966
throw unsupported_protocol("GPS does not support selected protocol");
970
sint16 *buf16 = (sint16 *) m_buf;
974
ident[6] = cmnt[40] = 0;
976
buf16[0] = host2garmin16((sint16) cmnd_transfer_prx);
977
m_ll->put_packet(pid_command_data, m_buf, 2);
979
packet_id pid = m_ll->get_packet(m_buf, sz);
981
if (pid != pid_records) {
982
throw not_possible("GPS did not reply with start of records data packet for proximity waypoint data");
985
throw not_possible("Proximity waypoint data start of records packet is wrong size");
988
sint16 num_recs = garmin2host16(buf16[0]);
989
prox_waypt_vec_t *waypts = new prox_waypt_vec_t(num_recs);
991
for (sint16 i = 0; i < num_recs; i++) {
992
proximity_waypt_type &waypt = (*waypts)[i];
994
pid = m_ll->get_packet(m_buf, sz);
996
if (pid != pid_prx_wpt_data) {
998
throw not_possible("GPS did not send proximity waypoint data packet");
1000
if (sz < (6 + 8 + 4 + 40 + 2 + 4)) {
1002
throw not_possible("Proximity waypoint data packet is too small");
1005
uint32 *buf32 = (uint32 *) (m_buf + 6);
1006
waypt.waypt.pos.lat = garmin2host32(buf32[0]);
1007
waypt.waypt.pos.lon = garmin2host32(buf32[1]);
1009
strncpy(ident, (char *) m_buf, 6);
1010
waypt.waypt.id = string(ident);
1012
strncpy(cmnt, (char *) m_buf + 18, 40);
1013
waypt.waypt.comment = string(cmnt);
1015
buf32 = (uint32 *) (m_buf + sz - 4);
1016
*buf32 = garmin2host32(buf32[0]);
1017
float32 *dist = (float32 *) buf32;
1021
pid = m_ll->get_packet(m_buf, sz);
1022
if (pid != pid_xfer_cmplt) {
1024
throw not_possible("GPS did not terminate proximity waypoint data with end of records packet");
1026
if (garmin2host16(buf16[0]) != cmnd_transfer_prx) {
1028
throw not_possible("End of records packet does not match original proximity waypoint transfer command");
1035
/////////////////////
1038
application_layer::send_proximity_waypoints(prox_waypt_vec_t *prx_waypts) throw (not_possible, timeout)
1042
product_data_type pd = get_product_data();
1044
if (!(pd.has_protocol('A',400) && pd.protocol_datatype('A',400,0) == 403))
1046
throw not_possible("Waypoint download not supported for this GPS");
1048
const int d103_sz = 6 + 8 + 4 + 40 + 2;
1050
sint16 *buf16 = (sint16 *) m_buf;
1051
buf16[0] = host2garmin16(prx_waypts->size());
1053
m_ll->put_packet(pid_records, m_buf, 2);
1055
for (unsigned int i = 0; i < prx_waypts->size(); i++) {
1056
basic_waypt_type &w = (*prx_waypts)[i].waypt;
1059
memset(m_buf, 0, d103_sz + 4);
1060
// set all waypt strings to blank spaces
1061
memset(m_buf, ' ', 6);
1062
memset(m_buf + 18, ' ', 40);
1064
// copy in strings, fixing up for GPS; leave bad characters as spaces
1065
for (size_t j = 0; j < min<size_t>(6, w.id.size()); j++) {
1068
m_buf[j] = toupper(c);
1071
for (size_t j = 0; j < min<size_t>(40, w.comment.length()); j++) {
1072
char c = w.comment[j];
1073
if (isalnum(c) || c == '-') {
1074
m_buf[j + 18] = toupper(c);
1078
// copy in position data
1079
uint32 *buf32 = (uint32 *) (m_buf + 6);
1080
buf32[0] = host2garmin32(w.pos.lat);
1081
buf32[1] = host2garmin32(w.pos.lon);
1083
// copy in proximity distance
1084
buf32 = (uint32 *) (m_buf + d103_sz);
1085
float32 d = (*prx_waypts)[i].dist;
1086
uint32 *t32 = (uint32 *) &d;
1087
*buf32 = host2garmin32(*t32);
1089
m_ll->put_packet(pid_prx_wpt_data, m_buf, d103_sz + 4);
1092
buf16[0] = host2garmin16(cmnd_transfer_prx);
1093
m_ll->put_packet(pid_xfer_cmplt, m_buf, 2);
1098
/////////////////////
1101
application_layer::turn_off_gps(void) throw (not_possible, timeout)
1105
sint16 *buf16 = (sint16 *) m_buf;
1107
buf16[0] = host2garmin16((sint16) cmnd_turn_off_pwr);
1108
m_ll->put_packet(pid_command_data, m_buf, 2);
1115
application_layer::get_date_time(void) throw (not_possible, timeout)
1119
sint16 *sbuf16 = (sint16 *) m_buf;
1121
sbuf16[0] = host2garmin16((sint16) cmnd_transfer_time);
1122
m_ll->put_packet(pid_command_data, m_buf, 2);
1125
packet_id pid = m_ll->get_packet(m_buf, sz);
1127
if (pid != pid_date_time_data) {
1128
throw not_possible("GPS did not respond with date and time packet");
1131
throw not_possible("Date and time packet is the wrong size");
1135
memset(&my_tm, 0, sizeof(struct tm));
1137
my_tm.tm_mon = m_buf[0] - 1;
1138
my_tm.tm_mday = m_buf[1];
1139
uint16 *ubuf16 = (uint16 *) m_buf;
1140
my_tm.tm_year = garmin2host16(ubuf16[1]) - 1900;
1141
my_tm.tm_hour = garmin2host16(sbuf16[2]);
1142
my_tm.tm_min = m_buf[6];
1143
my_tm.tm_sec = m_buf[7];
1145
my_tm.tm_isdst = -1;
1147
return mktime(&my_tm);
1150
/////////////////////
1153
application_layer::send_date_time(time_t t) throw (bad_time, not_possible, timeout)
1157
struct tm my_tm = *gmtime(&t);
1158
if (my_tm.tm_year < 90) {
1162
m_buf[0] = my_tm.tm_mon + 1;
1163
m_buf[1] = my_tm.tm_mday;
1164
uint16 *buf16 = (uint16 *) m_buf;;
1165
buf16[1] = host2garmin16(my_tm.tm_year + 1900);
1166
sint16 *sbuf16 = (sint16 *) buf16;
1167
sbuf16[2] = host2garmin16(my_tm.tm_hour);
1168
m_buf[6] = my_tm.tm_min;
1169
m_buf[8] = my_tm.tm_sec;
1171
m_ll->put_packet(pid_date_time_data, m_buf, 8);
1177
application_layer::get_position(void) throw (not_possible, timeout)
1181
sint16 *sbuf16 = (sint16 *) m_buf;
1183
sbuf16[0] = host2garmin16((sint16) cmnd_transfer_posn);
1184
m_ll->put_packet(pid_command_data, m_buf, 2);
1187
packet_id pid = m_ll->get_packet(m_buf, sz);
1189
if (pid != pid_position_data) {
1190
throw not_possible("GPS did not respond with position packet");
1193
throw not_possible("Position packet is the wrong size");
1196
double64 *dbuf = (double64 *) m_buf;
1198
retval.lat = garmin2host64(dbuf[0]);
1199
retval.lon = garmin2host64(dbuf[1]);
1204
/////////////////////
1207
application_layer::send_position(radian_type pos) throw (not_possible, timeout)
1211
double64 *dbuf = (double64 *) m_buf;
1212
dbuf[0] = host2garmin64(pos.lat);
1213
dbuf[1] = host2garmin64(pos.lon);
1215
m_ll->put_packet(pid_position_data, m_buf, 16);