My favorites | Sign in
Project Home Source
Checkout   Browse   Changes  
Changes to /trunk/gpsbabel/skytraq.c
r4176 vs. r4178 Compare: vs.  Format:
Revision r4178
Go to: 
/trunk/gpsbabel/skytraq.c   r4176 /trunk/gpsbabel/skytraq.c   r4178
1 /* 1 /*
2 2
3 Serial download of track data from GPS loggers with Skytraq chipset. 3 Serial download of track data from GPS loggers with Skytraq chipset.
4 4
5 Copyright (C) 2008-2009 Mathias Adam, m.adam (at) adamis.de 5 Copyright (C) 2008-2009 Mathias Adam, m.adam (at) adamis.de
6 6
7 2008 J.C Haessig, jean-christophe.haessig (at) dianosis.org 7 2008 J.C Haessig, jean-christophe.haessig (at) dianosis.org
8 2009-09-06 | Josef Reisinger | Added "set target location", i.e. -i skytrag,targetlocation=<lat>:<lng> 8 2009-09-06 | Josef Reisinger | Added "set target location", i.e. -i skytrag,targetlocation=<lat>:<lng>
9 2010-10-23 | Josef Reisinger | Added read/write for miniHomer POI 9 2010-10-23 | Josef Reisinger | Added read/write for miniHomer POI
10 10
11 This program is free software; you can redistribute it and/or modify 11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by 12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or 13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version. 14 (at your option) any later version.
15 15
16 This program is distributed in the hope that it will be useful, 16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 GNU General Public License for more details. 19 GNU General Public License for more details.
20 20
21 You should have received a copy of the GNU General Public License 21 You should have received a copy of the GNU General Public License
22 along with this program; if not, write to the Free Software 22 along with this program; if not, write to the Free Software
23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA 23 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
24 */ 24 */
25 25
26 #include <ctype.h> 26 #include <ctype.h>
27 27
28 #include "defs.h" 28 #include "defs.h"
29 #include "gbser.h" 29 #include "gbser.h"
30 //#include "jeeps/gpsmath.h" 30 //#include "jeeps/gpsmath.h"
31 31
32 #define MYNAME "skytraq" 32 #define MYNAME "skytraq"
33 33
34 #define TIMEOUT 5000 34 #define TIMEOUT 5000
35 #define SECTOR_SIZE 4096 35 #define SECTOR_SIZE 4096
36 #define FULL_ITEM_LEN 18 36 #define FULL_ITEM_LEN 18
37 #define COMPACT_ITEM_LEN 8 37 #define COMPACT_ITEM_LEN 8
38 38
39 /* Maximum number of chars to skip while waiting for a reply: */ 39 /* Maximum number of chars to skip while waiting for a reply: */
40 #define RETRIES 250 40 #define RETRIES 250
41 /* Maximum number of messages to read while expecting a specific message or ACK/NACK: */ 41 /* Maximum number of messages to read while expecting a specific message or ACK/NACK: */
42 #define MSG_RETRIES 3 42 #define MSG_RETRIES 3
43 /* Abort when reading a specific sector fails this many times: */ 43 /* Abort when reading a specific sector fails this many times: */
44 #define SECTOR_RETRIES 3 44 #define SECTOR_RETRIES 3
45 45
46 #define res_OK 0 46 #define res_OK 0
47 #define res_ERROR -1 47 #define res_ERROR -1
48 #define res_NACK -2 48 #define res_NACK -2
49 #define res_PROTOCOL_ERR -3 49 #define res_PROTOCOL_ERR -3
50 #define res_NOTFOUND -4 50 #define res_NOTFOUND -4
51 51
52 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y)) 52 #define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
53 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y)) 53 #define MAX(X,Y) ((X) > (Y) ? (X) : (Y))
54 54
55 55
56 static char *port; /* port name */ 56 static char *port; /* port name */
57 static void *serial_handle = 0; /* IO file descriptor */ 57 static void *serial_handle = 0; /* IO file descriptor */
58 static int skytraq_baud = 0; /* detected baud rate */ 58 static int skytraq_baud = 0; /* detected baud rate */
59 static gbfile *file_handle = 0; /* file descriptor (used by skytraq-bin format) */ 59 static gbfile *file_handle = 0; /* file descriptor (used by skytraq-bin format) */
60 60
61 static char *opt_erase = 0; /* erase after read? (0/1) */ 61 static char *opt_erase = 0; /* erase after read? (0/1) */
62 static char *opt_initbaud = 0; /* baud rate used to init device */ 62 static char *opt_initbaud = 0; /* baud rate used to init device */
63 static char *opt_dlbaud = 0; /* baud rate used for downloading tracks */ 63 static char *opt_dlbaud = 0; /* baud rate used for downloading tracks */
64 static char *opt_read_at_once = 0; /* number of sectors to read at once (Venus6 only) */ 64 static char *opt_read_at_once = 0; /* number of sectors to read at once (Venus6 only) */
65 static char *opt_first_sector = 0; /* first sector to be read from the device (default: 0) */ 65 static char *opt_first_sector = 0; /* first sector to be read from the device (default: 0) */
66 static char *opt_last_sector = 0; /* last sector to be read from the device (default: smart read everything) */ 66 static char *opt_last_sector = 0; /* last sector to be read from the device (default: smart read everything) */
67 static char *opt_dump_file = 0; /* dump raw data to this file (optional) */ 67 static char *opt_dump_file = 0; /* dump raw data to this file (optional) */
68 static char *opt_no_output = 0; /* disable output? (0/1) */ 68 static char *opt_no_output = 0; /* disable output? (0/1) */
69 static char *opt_set_location = 0; /* set if the "targetlocation" options was used */ 69 static char *opt_set_location = 0; /* set if the "targetlocation" options was used */
70 70
71 static 71 static
72 arglist_t skytraq_args[] = { 72 arglist_t skytraq_args[] = {
73 { 73 {
74 "erase", &opt_erase, "Erase device data after download", 74 "erase", &opt_erase, "Erase device data after download",
75 "0", ARGTYPE_BOOL, ARG_NOMINMAX 75 "0", ARGTYPE_BOOL, ARG_NOMINMAX
76 }, 76 },
77 { 77 {
78 "targetlocation", &opt_set_location, "Set location finder target location as lat,lng", 78 "targetlocation", &opt_set_location, "Set location finder target location as lat,lng",
79 "", ARGTYPE_STRING, "", "" 79 "", ARGTYPE_STRING, "", ""
80 }, 80 },
81 { 81 {
82 "baud", &opt_dlbaud, "Baud rate used for download", 82 "baud", &opt_dlbaud, "Baud rate used for download",
83 "230400", ARGTYPE_INT, "0", "230400" 83 "230400", ARGTYPE_INT, "0", "230400"
84 }, 84 },
85 { 85 {
86 "initbaud", &opt_initbaud, "Baud rate used to init device (0=autodetect)", 86 "initbaud", &opt_initbaud, "Baud rate used to init device (0=autodetect)",
87 "0", ARGTYPE_INT, "4800", "230400" 87 "0", ARGTYPE_INT, "4800", "230400"
88 }, 88 },
89 { 89 {
90 "read-at-once", &opt_read_at_once, "Number of sectors to read at once (0=use single sector mode)", 90 "read-at-once", &opt_read_at_once, "Number of sectors to read at once (0=use single sector mode)",
91 "255", ARGTYPE_INT, "0", "255" 91 "255", ARGTYPE_INT, "0", "255"
92 }, 92 },
93 { 93 {
94 "first-sector", &opt_first_sector, "First sector to be read from the device", 94 "first-sector", &opt_first_sector, "First sector to be read from the device",
95 "0", ARGTYPE_INT, "0", "65535" 95 "0", ARGTYPE_INT, "0", "65535"
96 }, 96 },
97 { 97 {
98 "last-sector", &opt_last_sector, "Last sector to be read from the device (-1: smart read everything)", 98 "last-sector", &opt_last_sector, "Last sector to be read from the device (-1: smart read everything)",
99 "-1", ARGTYPE_INT, "-1", "65535" 99 "-1", ARGTYPE_INT, "-1", "65535"
100 }, 100 },
101 { 101 {
102 "dump-file", &opt_dump_file, "Dump raw data to this file", 102 "dump-file", &opt_dump_file, "Dump raw data to this file",
103 NULL, ARGTYPE_OUTFILE, ARG_NOMINMAX 103 NULL, ARGTYPE_OUTFILE, ARG_NOMINMAX
104 }, 104 },
105 { 105 {
106 "no-output", &opt_no_output, "Disable output (useful with erase)", 106 "no-output", &opt_no_output, "Disable output (useful with erase)",
107 "0", ARGTYPE_BOOL, ARG_NOMINMAX 107 "0", ARGTYPE_BOOL, ARG_NOMINMAX
108 }, 108 },
109 ARG_TERMINATOR 109 ARG_TERMINATOR
110 }; 110 };
111 111
112 static 112 static
113 arglist_t skytraq_fargs[] = { 113 arglist_t skytraq_fargs[] = {
114 { 114 {
115 "first-sector", &opt_first_sector, "First sector to be read from the file", 115 "first-sector", &opt_first_sector, "First sector to be read from the file",
116 "0", ARGTYPE_INT, "0", "65535" 116 "0", ARGTYPE_INT, "0", "65535"
117 }, 117 },
118 { 118 {
119 "last-sector", &opt_last_sector, "Last sector to be read from the file (-1: read till empty sector)", 119 "last-sector", &opt_last_sector, "Last sector to be read from the file (-1: read till empty sector)",
120 "-1", ARGTYPE_INT, "-1", "65535" 120 "-1", ARGTYPE_INT, "-1", "65535"
121 }, 121 },
122 ARG_TERMINATOR 122 ARG_TERMINATOR
123 }; 123 };
124 124
125 static void 125 static void
126 db(int l, const char *msg, ...) 126 db(int l, const char *msg, ...)
127 { 127 {
128 va_list ap; 128 va_list ap;
129 va_start(ap, msg); 129 va_start(ap, msg);
130 if (global_opts.debug_level >= l) { 130 if (global_opts.debug_level >= l) {
131 vprintf(msg, ap); 131 vprintf(msg, ap);
132 } 132 }
133 va_end(ap); 133 va_end(ap);
134 } 134 }
135 135
136 static void 136 static void
137 rd_drain(void) 137 rd_drain(void)
138 { 138 {
139 if (gbser_flush(serial_handle)) { 139 if (gbser_flush(serial_handle)) {
140 db(1, MYNAME ": rd_drain(): Comm error\n"); 140 db(1, MYNAME ": rd_drain(): Comm error\n");
141 } 141 }
142 } 142 }
143 143
144 static int 144 static int
145 rd_char(int *errors) 145 rd_char(int *errors)
146 { 146 {
147 int c; 147 int c;
148 while (*errors > 0) { 148 while (*errors > 0) {
149 c = gbser_readc_wait(serial_handle, TIMEOUT); 149 c = gbser_readc_wait(serial_handle, TIMEOUT);
150 if (c < 0) { 150 if (c < 0) {
151 db(1, MYNAME ": rd_char(): Got error: %d\n", c); 151 db(1, MYNAME ": rd_char(): Got error: %d\n", c);
152 (*errors)--; 152 (*errors)--;
153 } else { 153 } else {
154 db(4, "rd_char(): Got char: %02x '%c'\n", c, isprint(c) ? c : '.'); 154 db(4, "rd_char(): Got char: %02x '%c'\n", c, isprint(c) ? c : '.');
155 return c; 155 return c;
156 } 156 }
157 } 157 }
158 fatal(MYNAME ": Too many read errors on serial port\n"); 158 fatal(MYNAME ": Too many read errors on serial port\n");
159 return -1; 159 return -1;
160 } 160 }
161 161
162 static int 162 static int
163 rd_buf(const gbuint8 *buf, int len) 163 rd_buf(const gbuint8 *buf, int len)
164 { 164 {
165 int rc, timeout, i; 165 int rc, timeout, i;
166 char dump[16*3+16+2]; 166 char dump[16*3+16+2];
167 167
168 /* Allow TIMEOUT plus the time needed to actually receive the data bytes: 168 /* Allow TIMEOUT plus the time needed to actually receive the data bytes:
169 * baudrate/10 bytes per second (8 data bits, start and stop bit) 169 * baudrate/10 bytes per second (8 data bits, start and stop bit)
170 * TODO: use dlbaud if selected. 170 * TODO: use dlbaud if selected.
171 */ 171 */
172 timeout = TIMEOUT + len;//*1000/(skytraq_baud/10); 172 timeout = TIMEOUT + len;//*1000/(skytraq_baud/10);
173 /*TODO: timeout gets <0 e.g. when len~=250000 --> 32bit signed int is too small. 173 /*TODO: timeout gets <0 e.g. when len~=250000 --> 32bit signed int is too small.
174 if (skytraq_baud > 0) timeout = TIMEOUT + (long long int)len*1000*10/(long long int)skytraq_baud; 174 if (skytraq_baud > 0) timeout = TIMEOUT + (long long int)len*1000*10/(long long int)skytraq_baud;
175 printf("len=%i skytraq_baud=%i timeout=%i\n", len, skytraq_baud, timeout);*/ 175 printf("len=%i skytraq_baud=%i timeout=%i\n", len, skytraq_baud, timeout);*/
176 rc = gbser_read_wait(serial_handle, (void*)buf, len, timeout); 176 rc = gbser_read_wait(serial_handle, (void*)buf, len, timeout);
177 if (rc < 0) { 177 if (rc < 0) {
178 db(1, MYNAME ": rd_buf(): Read error (%d)\n", rc); 178 db(1, MYNAME ": rd_buf(): Read error (%d)\n", rc);
179 return res_ERROR; 179 return res_ERROR;
180 } else if (rc < len) { 180 } else if (rc < len) {
181 db(1, MYNAME ": rd_buf(): Read timout\n"); 181 db(1, MYNAME ": rd_buf(): Read timout\n");
182 return res_ERROR; 182 return res_ERROR;
183 } 183 }
184 184
185 if (global_opts.debug_level >= 4) { 185 if (global_opts.debug_level >= 4) {
186 db(4, "rd_buf(): dump follows:\n"); 186 db(4, "rd_buf(): dump follows:\n");
187 dump[sizeof(dump)-1] = 0; 187 dump[sizeof(dump)-1] = 0;
188 for (i = 0; i < (len+15)/16*16; i++) { // count to next 16-byte boundary 188 for (i = 0; i < (len+15)/16*16; i++) { // count to next 16-byte boundary
189 if (i < len) { 189 if (i < len) {
190 snprintf(&dump[(i%16)*3], 4, "%02x ", buf[i]); 190 snprintf(&dump[(i%16)*3], 4, "%02x ", buf[i]);
191 snprintf(&dump[3*16+1+(i%16)], 2, "%c", isprint(buf[i]) ? buf[i] : '.'); 191 snprintf(&dump[3*16+1+(i%16)], 2, "%c", isprint(buf[i]) ? buf[i] : '.');
192 } else { 192 } else {
193 memset(&dump[(i%16)*3], ' ', 3); 193 memset(&dump[(i%16)*3], ' ', 3);
194 dump[3*16+1+(i%16)] = ' '; 194 dump[3*16+1+(i%16)] = ' ';
195 } 195 }
196 if ((i+1)%16 == 0) { 196 if ((i+1)%16 == 0) {
197 dump[16*3] = ' '; // gets overwritten with 0 by snprintf 197 dump[16*3] = ' '; // gets overwritten with 0 by snprintf
198 db(4, "%s\n", dump); 198 db(4, "%s\n", dump);
199 } 199 }
200 } 200 }
201 } 201 }
202 202
203 return res_OK; 203 return res_OK;
204 } 204 }
205 205
206 static int 206 static int
207 rd_word(void) 207 rd_word(void)
208 { 208 {
209 int errors = 5; /* allow this many errors */ 209 int errors = 5; /* allow this many errors */
210 gbuint8 buffer[2]; 210 gbuint8 buffer[2];
211 211
212 buffer[0] = rd_char(&errors); 212 buffer[0] = rd_char(&errors);
213 buffer[1] = rd_char(&errors); 213 buffer[1] = rd_char(&errors);
214 /* if (rd_buf(buffer, 2) != res_OK) { 214 /* if (rd_buf(buffer, 2) != res_OK) {
215 db(1, MYNAME ": rd_word(): Read error\n"); 215 db(1, MYNAME ": rd_word(): Read error\n");
216 return res_ERROR; 216 return res_ERROR;
217 }*/ 217 }*/
218 218
219 return (buffer[0] << 8) | buffer[1]; 219 return (buffer[0] << 8) | buffer[1];
220 } 220 }
221 221
222 static void 222 static void
223 wr_char(int c) 223 wr_char(int c)
224 { 224 {
225 int rc; 225 int rc;
226 db(4, "Sending: %02x '%c'\n", (unsigned)c, isprint(c) ? c : '.'); 226 db(4, "Sending: %02x '%c'\n", (unsigned)c, isprint(c) ? c : '.');
227 if (rc = gbser_writec(serial_handle, c), gbser_OK != rc) { 227 if (rc = gbser_writec(serial_handle, c), gbser_OK != rc) {
228 fatal(MYNAME ": Write error (%d)\n", rc); 228 fatal(MYNAME ": Write error (%d)\n", rc);
229 } 229 }
230 } 230 }
231 231
232 static void 232 static void
233 wr_buf(const unsigned char *str, int len) 233 wr_buf(const unsigned char *str, int len)
234 { 234 {
235 int i; 235 int i;
236 for (i = 0; i < len; i++) { 236 for (i = 0; i < len; i++) {
237 wr_char(str[i]); 237 wr_char(str[i]);
238 } 238 }
239 } 239 }
240 240
241 /******************************************************************************* 241 /*******************************************************************************
242 * %%% SkyTraq protocol implementation %%% * 242 * %%% SkyTraq protocol implementation %%% *
243 *******************************************************************************/ 243 *******************************************************************************/
244 244
245 gbuint8 NL[2] = { 0x0D, 0x0A }; 245 gbuint8 NL[2] = { 0x0D, 0x0A };
246 gbuint8 MSG_START[2] = { 0xA0, 0xA1 }; 246 gbuint8 MSG_START[2] = { 0xA0, 0xA1 };
247 gbuint8 SECTOR_READ_END[13] = { 'E','N','D', 0, 'C','H','E','C','K','S','U','M','=' }; 247 gbuint8 SECTOR_READ_END[13] = { 'E','N','D', 0, 'C','H','E','C','K','S','U','M','=' };
248 248
249 static int 249 static int
250 skytraq_calc_checksum(const unsigned char *buf, int len) 250 skytraq_calc_checksum(const unsigned char *buf, int len)
251 { 251 {
252 int i, cs = 0; 252 int i, cs = 0;
253 for (i = 0; i < len; i++) { 253 for (i = 0; i < len; i++) {
254 cs ^= buf[i]; 254 cs ^= buf[i];
255 } 255 }
256 return cs; 256 return cs;
257 } 257 }
258 258
259 static int 259 static int
260 skytraq_rd_msg(const void *payload, int len) 260 skytraq_rd_msg(const void *payload, int len)
261 { 261 {
262 int errors = 5; /* allow this many errors */ 262 int errors = 5; /* allow this many errors */
263 int c, i, state; 263 int c, i, state;
264 int rcv_len, calc_cs, rcv_cs; 264 int rcv_len, calc_cs, rcv_cs;
265 265
266 for (i = 0, state = 0; i < RETRIES && state < sizeof(MSG_START); i++) { 266 for (i = 0, state = 0; i < RETRIES && state < sizeof(MSG_START); i++) {
267 c = rd_char(&errors); 267 c = rd_char(&errors);
268 if (c == MSG_START[state]) { 268 if (c == MSG_START[state]) {
269 state++; 269 state++;
270 } else if (c == MSG_START[0]) { 270 } else if (c == MSG_START[0]) {
271 state = 1; 271 state = 1;
272 } else { 272 } else {
273 state = 0; 273 state = 0;
274 } 274 }
275 } 275 }
276 if (state < sizeof(MSG_START)) { 276 if (state < sizeof(MSG_START)) {
277 db(1, MYNAME ": Didn't get message start tag\n"); 277 db(1, MYNAME ": Didn't get message start tag\n");
278 return res_ERROR; 278 return res_ERROR;
279 } 279 }
280 280
281 if ((rcv_len = rd_word()) < len) { 281 if ((rcv_len = rd_word()) < len) {
282 if (rcv_len >= 0) { /* negative values indicate receive errors */ 282 if (rcv_len >= 0) { /* negative values indicate receive errors */
283 db(1, MYNAME ": Received message too short (got %i bytes, expected %i)\n", 283 db(1, MYNAME ": Received message too short (got %i bytes, expected %i)\n",
284 rcv_len, len); 284 rcv_len, len);
285 return res_PROTOCOL_ERR; 285 return res_PROTOCOL_ERR;
286 } 286 }
287 return res_ERROR; 287 return res_ERROR;
288 } 288 }
289 289
290 db(2, "Receiving message with %i bytes of payload (expected >=%i)\n", rcv_len, len); 290 db(2, "Receiving message with %i bytes of payload (expected >=%i)\n", rcv_len, len);
291 rd_buf((const unsigned char*) payload, MIN(rcv_len, len)); 291 rd_buf((const unsigned char*) payload, MIN(rcv_len, len));
292 292
293 calc_cs = skytraq_calc_checksum((const unsigned char*) payload, MIN(rcv_len, len)); 293 calc_cs = skytraq_calc_checksum((const unsigned char*) payload, MIN(rcv_len, len));
294 for (i = 0; i < rcv_len-len; i++) { 294 for (i = 0; i < rcv_len-len; i++) {
295 c = rd_char(&errors); 295 c = rd_char(&errors);
296 calc_cs ^= c; 296 calc_cs ^= c;
297 } 297 }
298 298
299 rcv_cs = rd_char(&errors); 299 rcv_cs = rd_char(&errors);
300 if (rcv_cs != calc_cs) { 300 if (rcv_cs != calc_cs) {
301 fatal(MYNAME ": Checksum error: got 0x%02x, expected 0x%02x\n", rcv_cs, calc_cs); 301 fatal(MYNAME ": Checksum error: got 0x%02x, expected 0x%02x\n", rcv_cs, calc_cs);
302 } 302 }
303 303
304 if (rd_word() != 0x0D0A) { 304 if (rd_word() != 0x0D0A) {
305 fatal(MYNAME ": Didn't get message end tag (CR/LF)\n"); 305 fatal(MYNAME ": Didn't get message end tag (CR/LF)\n");
306 } 306 }
307 307
308 // return MIN(rcv_len, len); 308 // return MIN(rcv_len, len);
309 return res_OK; 309 return res_OK;
310 } 310 }
311 311
312 static void 312 static void
313 skytraq_wr_msg(const gbuint8 *payload, int len) 313 skytraq_wr_msg(const gbuint8 *payload, int len)
314 { 314 {
315 int cs; 315 int cs;
316 316
317 rd_drain(); 317 rd_drain();
318 318
319 wr_buf(MSG_START, sizeof(MSG_START)); 319 wr_buf(MSG_START, sizeof(MSG_START));
320 wr_char((len>>8) & 0x0FF); 320 wr_char((len>>8) & 0x0FF);
321 wr_char(len & 0x0FF); 321 wr_char(len & 0x0FF);
322 wr_buf(payload, len); 322 wr_buf(payload, len);
323 323
324 cs = skytraq_calc_checksum(payload, len); 324 cs = skytraq_calc_checksum(payload, len);
325 wr_char(cs); 325 wr_char(cs);
326 wr_buf(NL, sizeof(NL)); 326 wr_buf(NL, sizeof(NL));
327 } 327 }
328 328
329 static int 329 static int
330 skytraq_expect_ack(gbuint8 id) 330 skytraq_expect_ack(gbuint8 id)
331 { 331 {
332 gbuint8 ack_msg[2]; 332 gbuint8 ack_msg[2];
333 int i/*, rcv_len*/; 333 int i/*, rcv_len*/;
334 334
335 for (i = 0; i < MSG_RETRIES; i++) { 335 for (i = 0; i < MSG_RETRIES; i++) {
336 // rcv_len = skytraq_rd_msg(ack_msg, sizeof(ack_msg)); 336 // rcv_len = skytraq_rd_msg(ack_msg, sizeof(ack_msg));
337 // if (rcv_len == sizeof(ack_msg)) { 337 // if (rcv_len == sizeof(ack_msg)) {
338 if (skytraq_rd_msg(ack_msg, sizeof(ack_msg)) == res_OK) { 338 if (skytraq_rd_msg(ack_msg, sizeof(ack_msg)) == res_OK) {
339 if (ack_msg[0] == 0x83) { 339 if (ack_msg[0] == 0x83) {
340 if (ack_msg[1] == id) { 340 if (ack_msg[1] == id) {
341 db(3, "Got ACK (id=0x%02x)\n", id); 341 db(3, "Got ACK (id=0x%02x)\n", id);
342 return res_OK; 342 return res_OK;
343 } else if (ack_msg[1] == 0) { 343 } else if (ack_msg[1] == 0) {
344 /* some (all?) devices first send an ACK with id==0, skip that */ 344 /* some (all?) devices first send an ACK with id==0, skip that */
345 continue; 345 continue;
346 } else { 346 } else {
347 db(1, MYNAME ": Warning: Got unexpected ACK (id=0x%02x)\n", ack_msg[1]); 347 db(1, MYNAME ": Warning: Got unexpected ACK (id=0x%02x)\n", ack_msg[1]);
348 continue; 348 continue;
349 } 349 }
350 } else if (ack_msg[0] == 0x84) { 350 } else if (ack_msg[0] == 0x84) {
351 db(3, "Warning: Got NACK (id=0x%02x)\n", ack_msg[1]); 351 db(3, "Warning: Got NACK (id=0x%02x)\n", ack_msg[1]);
352 return res_NACK; 352 return res_NACK;
353 } else { 353 } else {
354 db(3, "Warning: Got unexpected message (id=0x%02x), expected ACK (id=0x%02x)\n", 354 db(3, "Warning: Got unexpected message (id=0x%02x), expected ACK (id=0x%02x)\n",
355 ack_msg[0], id); 355 ack_msg[0], id);
356 } 356 }
357 } else { 357 } else {
358 /* payload too short or didn't receive a message at all 358 /* payload too short or didn't receive a message at all
359 -> caller should either resend request or give up. 359 -> caller should either resend request or give up.
360 */ 360 */
361 break; 361 break;
362 } 362 }
363 } 363 }
364 364
365 return res_PROTOCOL_ERR; 365 return res_PROTOCOL_ERR;
366 } 366 }
367 367
368 static int 368 static int
369 skytraq_expect_msg(gbuint8 id, const gbuint8 *payload, int len) 369 skytraq_expect_msg(gbuint8 id, const gbuint8 *payload, int len)
370 { 370 {
371 int i, rc; 371 int i, rc;
372 372
373 for (i = 0; i < MSG_RETRIES; i++) { 373 for (i = 0; i < MSG_RETRIES; i++) {
374 rc = skytraq_rd_msg(payload, len); 374 rc = skytraq_rd_msg(payload, len);
375 if (rc < 0) { 375 if (rc < 0) {
376 return rc; 376 return rc;
377 } 377 }
378 if (payload[0] == id) { 378 if (payload[0] == id) {
379 return len; 379 return len;
380 } 380 }
381 } 381 }
382 382
383 return res_PROTOCOL_ERR; 383 return res_PROTOCOL_ERR;
384 } 384 }
385 385
386 static int 386 static int
387 skytraq_wr_msg_verify(const gbuint8 *payload, int len) 387 skytraq_wr_msg_verify(const gbuint8 *payload, int len)
388 { 388 {
389 int i, rc; 389 int i, rc;
390 390
391 for (i = 0; i < MSG_RETRIES; i++) { 391 for (i = 0; i < MSG_RETRIES; i++) {
392 if (i > 0) { 392 if (i > 0) {
393 db(1, "resending msg (id=0x%02x)...\n", payload[0]); 393 db(1, "resending msg (id=0x%02x)...\n", payload[0]);
394 } 394 }
395 skytraq_wr_msg(payload, len); 395 skytraq_wr_msg(payload, len);
396 rc = skytraq_expect_ack(payload[0]); 396 rc = skytraq_expect_ack(payload[0]);
397 if (rc == res_OK || rc == res_NACK) { 397 if (rc == res_OK || rc == res_NACK) {
398 return rc; 398 return rc;
399 } 399 }
400 db(1, MYNAME ": Got neither ACK nor NACK, "); 400 db(1, MYNAME ": Got neither ACK nor NACK, ");
401 } 401 }
402 db(1, "aborting (msg id was 0x%02x).\n", payload[0]); 402 db(1, "aborting (msg id was 0x%02x).\n", payload[0]);
403 403
404 return res_ERROR; 404 return res_ERROR;
405 } 405 }
406 406
407 static int 407 static int
408 skytraq_system_restart(void) 408 skytraq_system_restart(void)
409 { 409 {
410 gbuint8 MSG_SYSTEM_RESTART[15] = 410 gbuint8 MSG_SYSTEM_RESTART[15] =
411 { 0x01, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 411 { 0x01, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
412 412
413 db(2, "restart system\n"); 413 db(2, "restart system\n");
414 return skytraq_wr_msg_verify(MSG_SYSTEM_RESTART, sizeof(MSG_SYSTEM_RESTART)); 414 return skytraq_wr_msg_verify(MSG_SYSTEM_RESTART, sizeof(MSG_SYSTEM_RESTART));
415 } 415 }
416 416
417 static int 417 static int
418 skytraq_set_baud(int baud) 418 skytraq_set_baud(int baud)
419 { 419 {
420 /* Note: according to AN0003_v3.pdf, attrib == 0x00 means write to SRAM only, however 420 /* Note: according to AN0003_v3.pdf, attrib == 0x00 means write to SRAM only, however
421 * it seems to write to flash too. The Windows software sends 0x02 so we do here too. 421 * it seems to write to flash too. The Windows software sends 0x02 so we do here too.
422 */ 422 */
423 gbuint8 MSG_CONFIGURE_SERIAL_PORT[4] 423 gbuint8 MSG_CONFIGURE_SERIAL_PORT[4]
424 = { 0x05, 0x00, 0x00, 0x02 }; 424 = { 0x05, 0x00, 0x00, 0x02 };
425 int rc; 425 int rc;
426 426
427 db(2, "Setting baud rate to %i\n", baud); 427 db(2, "Setting baud rate to %i\n", baud);
428 428
429 switch (baud) { 429 switch (baud) {
430 case 4800: 430 case 4800:
431 MSG_CONFIGURE_SERIAL_PORT[2] = 0; 431 MSG_CONFIGURE_SERIAL_PORT[2] = 0;
432 break; 432 break;
433 case 9600: 433 case 9600:
434 MSG_CONFIGURE_SERIAL_PORT[2] = 1; 434 MSG_CONFIGURE_SERIAL_PORT[2] = 1;
435 break; 435 break;
436 case 19200: 436 case 19200:
437 MSG_CONFIGURE_SERIAL_PORT[2] = 2; 437 MSG_CONFIGURE_SERIAL_PORT[2] = 2;
438 break; 438 break;
439 case 38400: 439 case 38400:
440 MSG_CONFIGURE_SERIAL_PORT[2] = 3; 440 MSG_CONFIGURE_SERIAL_PORT[2] = 3;
441 break; 441 break;
442 case 57600: 442 case 57600:
443 MSG_CONFIGURE_SERIAL_PORT[2] = 4; 443 MSG_CONFIGURE_SERIAL_PORT[2] = 4;
444 break; 444 break;
445 case 115200: 445 case 115200:
446 MSG_CONFIGURE_SERIAL_PORT[2] = 5; 446 MSG_CONFIGURE_SERIAL_PORT[2] = 5;
447 break; 447 break;
448 case 230400: 448 case 230400:
449 MSG_CONFIGURE_SERIAL_PORT[2] = 6; 449 MSG_CONFIGURE_SERIAL_PORT[2] = 6;
450 break; 450 break;
451 default: 451 default:
452 fatal(MYNAME ": Unsupported baud rate: %ibd\n", baud); 452 fatal(MYNAME ": Unsupported baud rate: %ibd\n", baud);
453 } 453 }
454 454
455 rc = skytraq_wr_msg_verify(MSG_CONFIGURE_SERIAL_PORT, sizeof(MSG_CONFIGURE_SERIAL_PORT)); 455 rc = skytraq_wr_msg_verify(MSG_CONFIGURE_SERIAL_PORT, sizeof(MSG_CONFIGURE_SERIAL_PORT));
456 if (rc != res_OK) { 456 if (rc != res_OK) {
457 db(2, "Warning: error setting skytraq device baud rate\n"); 457 db(2, "Warning: error setting skytraq device baud rate\n");
458 return rc; 458 return rc;
459 } 459 }
460 460
461 db(3, "Now setting UART baud rate to %i\n", baud); 461 db(3, "Now setting UART baud rate to %i\n", baud);
462 rd_drain(); 462 rd_drain();
463 if (gbser_set_speed(serial_handle, baud) != gbser_OK) { 463 if (gbser_set_speed(serial_handle, baud) != gbser_OK) {
464 db(2, "Warning: error setting uart baud rate\n"); 464 db(2, "Warning: error setting uart baud rate\n");
465 return res_ERROR; 465 return res_ERROR;
466 } 466 }
467 467
468 gb_sleep(50); /* allow UART to settle. */ 468 gb_sleep(50); /* allow UART to settle. */
469 469
470 return res_OK; 470 return res_OK;
471 } 471 }
472 472
473 static int 473 static int
474 skytraq_get_log_buffer_status(gbuint32 *log_wr_ptr, gbuint16 *sectors_free, gbuint16 *sectors_total) 474 skytraq_get_log_buffer_status(gbuint32 *log_wr_ptr, gbuint16 *sectors_free, gbuint16 *sectors_total)
475 { 475 {
476 gbuint8 MSG_LOG_STATUS_CONTROL = 0x17; 476 gbuint8 MSG_LOG_STATUS_CONTROL = 0x17;
477 struct { 477 struct {
478 gbuint8 id[1]; 478 gbuint8 id[1];
479 gbuint8 log_wr_ptr[4]; 479 gbuint8 log_wr_ptr[4];
480 gbuint8 sectors_free[2]; 480 gbuint8 sectors_free[2];
481 gbuint8 sectors_total[2]; 481 gbuint8 sectors_total[2];
482 } MSG_LOG_STATUS_OUTPUT; 482 } MSG_LOG_STATUS_OUTPUT;
483 int rc; 483 int rc;
484 484
485 if ((rc = skytraq_wr_msg_verify(&MSG_LOG_STATUS_CONTROL, 1)) != res_OK) { /* get memory status */ 485 if ((rc = skytraq_wr_msg_verify(&MSG_LOG_STATUS_CONTROL, 1)) != res_OK) { /* get memory status */
486 db(1, MYNAME ": Error sending LOG STATUS CONTROL message (%d)\n", rc); 486 db(1, MYNAME ": Error sending LOG STATUS CONTROL message (%d)\n", rc);
487 return res_ERROR; 487 return res_ERROR;
488 } 488 }
489 489
490 rc = skytraq_expect_msg(0x94, (gbuint8*)&MSG_LOG_STATUS_OUTPUT, sizeof(MSG_LOG_STATUS_OUTPUT)); 490 rc = skytraq_expect_msg(0x94, (gbuint8*)&MSG_LOG_STATUS_OUTPUT, sizeof(MSG_LOG_STATUS_OUTPUT));
491 if (rc < sizeof(MSG_LOG_STATUS_OUTPUT)) { 491 if (rc < sizeof(MSG_LOG_STATUS_OUTPUT)) {
492 db(1, MYNAME ": Didn't receive expected reply (%d)\n", rc); 492 db(1, MYNAME ": Didn't receive expected reply (%d)\n", rc);
493 return res_ERROR; 493 return res_ERROR;
494 } 494 }
495 495
496 *log_wr_ptr = le_readu32(&MSG_LOG_STATUS_OUTPUT.log_wr_ptr); 496 *log_wr_ptr = le_readu32(&MSG_LOG_STATUS_OUTPUT.log_wr_ptr);
497 *sectors_free = le_readu16(&MSG_LOG_STATUS_OUTPUT.sectors_free); 497 *sectors_free = le_readu16(&MSG_LOG_STATUS_OUTPUT.sectors_free);
498 *sectors_total = le_readu16(&MSG_LOG_STATUS_OUTPUT.sectors_total); 498 *sectors_total = le_readu16(&MSG_LOG_STATUS_OUTPUT.sectors_total);
499 499
500 return res_OK; 500 return res_OK;
501 } 501 }
502 502
503 /* reads 32-bit "middle-endian" fields */ 503 /* reads 32-bit "middle-endian" fields */
504 static unsigned int me_read32(const unsigned char *p) 504 static unsigned int me_read32(const unsigned char *p)
505 { 505 {
506 return ((unsigned)be_read16(p+2) << 16) | ((unsigned)be_read16(p)); 506 return ((unsigned)be_read16(p+2) << 16) | ((unsigned)be_read16(p));
507 } 507 }
508 508
509 struct read_state { 509 struct read_state {
510 route_head *route_head_; 510 route_head *route_head_;
511 unsigned wpn, tpn; 511 unsigned wpn, tpn;
512 512
513 time_t ts; 513 time_t ts;
514 long x, y, z; 514 long x, y, z;
515 }; 515 };
516 516
517 static void 517 static void
518 state_init(struct read_state *pst) 518 state_init(struct read_state *pst)
519 { 519 {
520 route_head *track; 520 route_head *track;
521 521
522 track = route_head_alloc(); 522 track = route_head_alloc();
523 track->rte_name = xstrdup("SkyTraq tracklog"); 523 track->rte_name = xstrdup("SkyTraq tracklog");
524 track->rte_desc = xstrdup("SkyTraq GPS tracklog data"); 524 track->rte_desc = xstrdup("SkyTraq GPS tracklog data");
525 track_add_head(track); 525 track_add_head(track);
526 526
527 pst->route_head_ = track; 527 pst->route_head_ = track;
528 pst->wpn = 0; 528 pst->wpn = 0;
529 pst->tpn = 0; 529 pst->tpn = 0;
530 530
531 pst->ts = 0; 531 pst->ts = 0;
532 pst->x = 0; 532 pst->x = 0;
533 pst->y = 0; 533 pst->y = 0;
534 pst->z = 0; 534 pst->z = 0;
535 } 535 }
536 536
537 static waypoint * 537 static waypoint *
538 make_trackpoint(struct read_state *st, double lat, double lon, double alt) 538 make_trackpoint(struct read_state *st, double lat, double lon, double alt)
539 { 539 {
540 waypoint *wpt = waypt_new(); 540 waypoint *wpt = waypt_new();
541 541
542 xasprintf(&wpt->shortname, "TP%04d", ++st->tpn); 542 xasprintf(&wpt->shortname, "TP%04d", ++st->tpn);
543 543
544 wpt->latitude = lat;; 544 wpt->latitude = lat;;
545 wpt->longitude = lon; 545 wpt->longitude = lon;
546 wpt->altitude = alt; 546 wpt->altitude = alt;
547 wpt->creation_time = st->ts; 547 wpt->creation_time = st->ts;
548 548
549 return wpt; 549 return wpt;
550 } 550 }
551 551
552 static time_t 552 static time_t
553 gpstime_to_timet(int week, int sec) 553 gpstime_to_timet(int week, int sec)
554 { 554 {
555 /* TODO: make leap second compensation more general 555 /* TODO: make leap second compensation more general
556 * (the windows software seems to correct by 13). 556 * (the windows software seems to correct by 13).
557 */ 557 */
558 return (315964800 + (week+1024)*7*SECONDS_PER_DAY + sec - 13); 558 return (315964800 + (week+1024)*7*SECONDS_PER_DAY + sec - 13);
559 } 559 }
560 560
561 static void 561 static void
562 ECEF_to_LLA(double x, double y, long z, double *lat, double *lon, double *alt) 562 ECEF_to_LLA(double x, double y, long z, double *lat, double *lon, double *alt)
563 { 563 {
564 /* constants: */ 564 /* constants: */
565 #define CA 6378137.0 565 #define CA 6378137.0
566 #define CB 6356752.31424518 566 #define CB 6356752.31424518
567 #define CE2 (CA*CA - CB*CB) / (CA*CA) /* =e^2 */ 567 #define CE2 (CA*CA - CB*CB) / (CA*CA) /* =e^2 */
568 #define CE_2 (CA*CA - CB*CB) / (CB*CB) /* =e'^2 */ 568 #define CE_2 (CA*CA - CB*CB) / (CB*CB) /* =e'^2 */
569 /* auxiliary values: */ 569 /* auxiliary values: */
570 #define AP sqrt(x*x + y*y) 570 #define AP sqrt(x*x + y*y)
571 #define ATHETA atan2(z*CA, AP*CB) 571 #define ATHETA atan2(z*CA, AP*CB)
572 #define AN CA / sqrt(1 - CE2 * pow(sin(*lat), 2)) /* must calc *lat before using AN! */ 572 #define AN CA / sqrt(1 - CE2 * pow(sin(*lat), 2)) /* must calc *lat before using AN! */
573 573
574 // latitude (in radians): 574 // latitude (in radians):
575 *lat = atan2(z + CE_2 * CB * pow(sin(ATHETA), 3), AP - CE2 * CA * pow(cos(ATHETA), 3)); 575 *lat = atan2(z + CE_2 * CB * pow(sin(ATHETA), 3), AP - CE2 * CA * pow(cos(ATHETA), 3));
576 576
577 // longitude (in radians): 577 // longitude (in radians):
578 *lon = atan2(y, x); 578 *lon = atan2(y, x);
579 579
580 // height above ellipsoid (in meters): 580 // height above ellipsoid (in meters):
581 *alt = AP/cos(*lat) - AN; 581 *alt = AP/cos(*lat) - AN;
582 582
583 *lat = *lat /M_PI*180; 583 *lat = *lat /M_PI*180;
584 *lon = *lon /M_PI*180; 584 *lon = *lon /M_PI*180;
585 } 585 }
586 586
587 typedef struct { 587 typedef struct {
588 gbuint32 gps_week; 588 gbuint32 gps_week;
589 gbuint32 gps_sec; 589 gbuint32 gps_sec;
590 gbint32 x; 590 gbint32 x;
591 gbint32 y; 591 gbint32 y;
592 gbint32 z; 592 gbint32 z;
593 } full_item; 593 } full_item;
594 594
595 typedef struct { 595 typedef struct {
596 gbint16 dt; 596 gbint16 dt;
597 gbint16 dx; 597 gbint16 dx;
598 gbint16 dy; 598 gbint16 dy;
599 gbint16 dz; 599 gbint16 dz;
600 } compact_item; 600 } compact_item;
601 601
602 struct full_item_frame { 602 struct full_item_frame {
603 unsigned char ts[4]; 603 unsigned char ts[4];
604 unsigned char x[4]; 604 unsigned char x[4];
605 unsigned char y[4]; 605 unsigned char y[4];
606 unsigned char z[4]; 606 unsigned char z[4];
607 }; 607 };
608 struct compact_item_frame { 608 struct compact_item_frame {
609 unsigned char dt[2]; /* big endian unsigned short */ 609 unsigned char dt[2]; /* big endian unsigned short */
610 unsigned char dpos[4]; 610 unsigned char dpos[4];
611 }; 611 };
612 612
613 typedef struct { 613 typedef struct {
614 unsigned char type_and_speed[2]; 614 unsigned char type_and_speed[2];
615 union { 615 union {
616 struct full_item_frame full; 616 struct full_item_frame full;
617 struct compact_item_frame comp; 617 struct compact_item_frame comp;
618 }; 618 };
619 } item_frame; 619 } item_frame;
620 620
621 #define ITEM_TYPE(item) (item->type_and_speed[0] >> 4) 621 #define ITEM_TYPE(item) (item->type_and_speed[0] >> 4)
622 #define ITEM_SPEED(item) (item->type_and_speed[1] | ((item->type_and_speed[0] & 0x0F) << 8)) 622 #define ITEM_SPEED(item) (item->type_and_speed[1] | ((item->type_and_speed[0] & 0x0F) << 8))
623 623
624 static int 624 static int
625 process_data_item(struct read_state *pst, const item_frame *pitem, int len) 625 process_data_item(struct read_state *pst, const item_frame *pitem, int len)
626 { 626 {
627 int res = 0; 627 int res = 0;
628 double lat, lon, alt; 628 double lat, lon, alt;
629 gbint32 ts; 629 unsigned int ts;
630 int poi = 0; 630 int poi = 0;
631 full_item f; 631 full_item f;
632 compact_item c; 632 compact_item c;
633 waypoint *tpt = NULL; 633 waypoint *tpt = NULL;
634 634
635 switch (ITEM_TYPE(pitem)) { 635 switch (ITEM_TYPE(pitem)) {
636 case 0x6: /* POI item (same structure as full) */ 636 case 0x6: /* POI item (same structure as full) */
637 poi = 1; 637 poi = 1;
638 /* fall through: */ 638 /* fall through: */
639 639
640 case 0x4: /* full item */ 640 case 0x4: /* full item */
641 if (len < FULL_ITEM_LEN) { 641 if (len < FULL_ITEM_LEN) {
642 db(1, MYNAME ": Not enough bytes in sector for a full item.\n"); 642 db(1, MYNAME ": Not enough bytes in sector for a full item.\n");
643 return res_ERROR; 643 return res_ERROR;
644 } 644 }
645 ts = me_read32(pitem->full.ts); 645 ts = me_read32(pitem->full.ts);
646 f.gps_week = ts & 0x000003FF; 646 f.gps_week = ts & 0x000003FF;
647 f.gps_sec = ts >> 12; 647 f.gps_sec = ts >> 12;
648 f.x = me_read32(pitem->full.x); 648 f.x = me_read32(pitem->full.x);
649 f.y = me_read32(pitem->full.y); 649 f.y = me_read32(pitem->full.y);
650 f.z = me_read32(pitem->full.z); 650 f.z = me_read32(pitem->full.z);
651 651
652 pst->ts = gpstime_to_timet(f.gps_week, f.gps_sec); 652 pst->ts = gpstime_to_timet(f.gps_week, f.gps_sec);
653 pst->x = f.x; 653 pst->x = f.x;
654 pst->y = f.y; 654 pst->y = f.y;
655 pst->z = f.z; 655 pst->z = f.z;
656 656
657 db(4, "Got %s item: week=%i sec=%i (time_t=%i) x=%i y=%i z=%i speed=%i\n", 657 db(4, "Got %s item: week=%i sec=%i (time_t=%i) x=%i y=%i z=%i speed=%i\n",
658 poi ? "POI" : "full", 658 poi ? "POI" : "full",
659 f.gps_week, f.gps_sec, pst->ts, 659 f.gps_week, f.gps_sec, pst->ts,
660 f.x, f.y, f.z, 660 f.x, f.y, f.z,
661 ITEM_SPEED(pitem)); 661 ITEM_SPEED(pitem));
662 662
663 res = FULL_ITEM_LEN; 663 res = FULL_ITEM_LEN;
664 break; 664 break;
665 665
666 case 0x8: /* compact item */ 666 case 0x8: /* compact item */
667 if (len < COMPACT_ITEM_LEN) { 667 if (len < COMPACT_ITEM_LEN) {
668 db(1, MYNAME ": Not enough bytes in sector for a compact item.\n"); 668 db(1, MYNAME ": Not enough bytes in sector for a compact item.\n");
669 return res_ERROR; 669 return res_ERROR;
670 } 670 }
671 c.dx = (pitem->comp.dpos[1] >> 6) | (pitem->comp.dpos[0] << 2); 671 c.dx = (pitem->comp.dpos[1] >> 6) | (pitem->comp.dpos[0] << 2);
672 c.dy = (pitem->comp.dpos[1] & 0x3F) | ((pitem->comp.dpos[2] & 0xF0) << 2); 672 c.dy = (pitem->comp.dpos[1] & 0x3F) | ((pitem->comp.dpos[2] & 0xF0) << 2);
673 c.dz = pitem->comp.dpos[3] | ((pitem->comp.dpos[2] & 0x03) << 8); 673 c.dz = pitem->comp.dpos[3] | ((pitem->comp.dpos[2] & 0x03) << 8);
674 if (c.dx > 511) { 674 if (c.dx > 511) {
675 c.dx = 511-c.dx; /* make proper signed values */ 675 c.dx = 511-c.dx; /* make proper signed values */
676 } 676 }
677 if (c.dy > 511) { 677 if (c.dy > 511) {
678 c.dy = 511-c.dy; 678 c.dy = 511-c.dy;
679 } 679 }
680 if (c.dz > 511) { 680 if (c.dz > 511) {
681 c.dz = 511-c.dz; 681 c.dz = 511-c.dz;
682 } 682 }
683 c.dt = (pitem->comp.dt[0] << 8) | pitem->comp.dt[1]; 683 c.dt = (pitem->comp.dt[0] << 8) | pitem->comp.dt[1];
684 684
685 db(4, "Got compact item: dt=%i dx=%i dy=%i dz=%i speed=%i uu=%i\n", 685 db(4, "Got compact item: dt=%i dx=%i dy=%i dz=%i speed=%i uu=%i\n",
686 c.dt, c.dx, c.dy, c.dz, 686 c.dt, c.dx, c.dy, c.dz,
687 ITEM_SPEED(pitem), (pitem->comp.dpos[2] & 0x0F)>>2); 687 ITEM_SPEED(pitem), (pitem->comp.dpos[2] & 0x0F)>>2);
688 688
689 pst->ts += c.dt; 689 pst->ts += c.dt;
690 pst->x += c.dx; 690 pst->x += c.dx;
691 pst->y += c.dy; 691 pst->y += c.dy;
692 pst->z += c.dz; 692 pst->z += c.dz;
693 693
694 res = COMPACT_ITEM_LEN; 694 res = COMPACT_ITEM_LEN;
695 break; 695 break;
696 696
697 default: 697 default:
698 db(1, MYNAME ": Unknown item type encountered: 0x%02x\n", ITEM_TYPE(pitem)); 698 db(1, MYNAME ": Unknown item type encountered: 0x%02x\n", ITEM_TYPE(pitem));
699 return 0; 699 return 0;
700 } 700 }
701 701
702 if (res == COMPACT_ITEM_LEN || res == FULL_ITEM_LEN) { 702 if (res == COMPACT_ITEM_LEN || res == FULL_ITEM_LEN) {
703 ECEF_to_LLA(pst->x, pst->y, pst->z, &lat, &lon, &alt); 703 ECEF_to_LLA(pst->x, pst->y, pst->z, &lat, &lon, &alt);
704 // GPS_Math_XYZ_To_WGS84LatLonH(&lat, &lon, &alt, pst->x, pst->y, pst->z); 704 // GPS_Math_XYZ_To_WGS84LatLonH(&lat, &lon, &alt, pst->x, pst->y, pst->z);
705 tpt = make_trackpoint(pst, lat, lon, alt); 705 tpt = make_trackpoint(pst, lat, lon, alt);
706 WAYPT_SET(tpt, speed, KPH_TO_MPS(ITEM_SPEED(pitem))); /* convert speed to m/s */ 706 WAYPT_SET(tpt, speed, KPH_TO_MPS(ITEM_SPEED(pitem))); /* convert speed to m/s */
707 707
708 if (poi) { 708 if (poi) {
709 waypt_add(waypt_dupe(tpt)); 709 waypt_add(waypt_dupe(tpt));
710 } 710 }
711 711
712 if (0 == pst->route_head_) { 712 if (0 == pst->route_head_) {
713 db(1, MYNAME ": New Track\n"); 713 db(1, MYNAME ": New Track\n");
714 pst->route_head_ = route_head_alloc(); 714 pst->route_head_ = route_head_alloc();
715 track_add_head(pst->route_head_); 715 track_add_head(pst->route_head_);
716 } 716 }
717 717
718 track_add_wpt(pst->route_head_, tpt); 718 track_add_wpt(pst->route_head_, tpt);
719 } 719 }
720 720
721 return res; 721 return res;
722 } 722 }
723 723
724 static int /* returns number of bytes processed (terminates on 0xFF i.e. empty or padding bytes) */ 724 static int /* returns number of bytes processed (terminates on 0xFF i.e. empty or padding bytes) */
725 process_data_sector(struct read_state *pst, const gbuint8 *buf, int len) 725 process_data_sector(struct read_state *pst, const gbuint8 *buf, int len)
726 { 726 {
727 int plen, ilen; 727 int plen, ilen;
728 728
729 for (plen = 0; plen < len && buf[plen] != 0xFF; plen += ilen) { 729 for (plen = 0; plen < len && buf[plen] != 0xFF; plen += ilen) {
730 ilen = process_data_item(pst, (item_frame*)&buf[plen], len-plen); 730 ilen = process_data_item(pst, (item_frame*)&buf[plen], len-plen);
731 if (ilen <= 0) { 731 if (ilen <= 0) {
732 fatal(MYNAME ": Error %i while processing data item #%i (starts at %i)\n", 732 fatal(MYNAME ": Error %i while processing data item #%i (starts at %i)\n",
733 ilen, pst->tpn, plen); 733 ilen, pst->tpn, plen);
734 } 734 }
735 } 735 }
736 736
737 return plen; 737 return plen;
738 } 738 }
739 739
740 /* Note: the buffer is being padded with 0xFFs if necessary so there are always SECTOR_SIZE valid bytes */ 740 /* Note: the buffer is being padded with 0xFFs if necessary so there are always SECTOR_SIZE valid bytes */
741 static int 741 static int
742 skytraq_read_single_sector(int sector, gbuint8 *buf) 742 skytraq_read_single_sector(int sector, gbuint8 *buf)
743 { 743 {
744 gbuint8 MSG_LOG_SECTOR_READ_CONTROL[2] = { 0x1B, sector }; 744 gbuint8 MSG_LOG_SECTOR_READ_CONTROL[2] = { 0x1B, sector };
745 int errors = 5; /* allow this many errors */ 745 int errors = 5; /* allow this many errors */
746 int c, i, j, cs; 746 int c, i, j, cs;
747 gbuint8 buffer[16]; 747 gbuint8 buffer[16];
748 748
749 if (sector < 0 || sector > 0xFF) { 749 if (sector < 0 || sector > 0xFF) {
750 fatal(MYNAME ": Invalid sector number (%i)\n", sector); 750 fatal(MYNAME ": Invalid sector number (%i)\n", sector);
751 } 751 }
752 752
753 db(2, "Reading sector #%i...\n", sector); 753 db(2, "Reading sector #%i...\n", sector);
754 754
755 if (skytraq_wr_msg_verify((gbuint8*)&MSG_LOG_SECTOR_READ_CONTROL, sizeof(MSG_LOG_SECTOR_READ_CONTROL)) != res_OK) { 755 if (skytraq_wr_msg_verify((gbuint8*)&MSG_LOG_SECTOR_READ_CONTROL, sizeof(MSG_LOG_SECTOR_READ_CONTROL)) != res_OK) {
756 db(1, MYNAME ": Didn't receive ACK\n"); 756 db(1, MYNAME ": Didn't receive ACK\n");
757 return res_ERROR; 757 return res_ERROR;
758 } 758 }
759 759
760 #ifdef READ_SINGLE_CHARS 760 #ifdef READ_SINGLE_CHARS
761 for (i = 0, j = 0; i-j < SECTOR_SIZE && j < sizeof(SECTOR_READ_END); i++) { 761 for (i = 0, j = 0; i-j < SECTOR_SIZE && j < sizeof(SECTOR_READ_END); i++) {
762 c = rd_char(&errors); 762 c = rd_char(&errors);
763 buf[i] = c; 763 buf[i] = c;
764 if (c == SECTOR_READ_END[j]) { 764 if (c == SECTOR_READ_END[j]) {
765 j++; 765 j++;
766 } else if (c == SECTOR_READ_END[0]) { 766 } else if (c == SECTOR_READ_END[0]) {
767 j = 1; 767 j = 1;
768 } else { 768 } else {
769 j = 0; 769 j = 0;
770 } 770 }
771 } 771 }
772 if (j < sizeof(SECTOR_READ_END)) { 772 if (j < sizeof(SECTOR_READ_END)) {
773 db(1, MYNAME ": Didn't get sector end tag\n"); 773 db(1, MYNAME ": Didn't get sector end tag\n");
774 return res_ERROR; 774 return res_ERROR;
775 } 775 }
776 c = rd_char(&errors); /* read checksum byte */ 776 c = rd_char(&errors); /* read checksum byte */
777 buf[i] = c; 777 buf[i] = c;
778 #else 778 #else
779 for (i = 0, j = 0; i-j < SECTOR_SIZE && j < sizeof(SECTOR_READ_END); i+=c) { 779 for (i = 0, j = 0; i-j < SECTOR_SIZE && j < sizeof(SECTOR_READ_END); i+=c) {
780 rd_buf(buffer, 16); 780 rd_buf(buffer, 16);
781 for (c = 0; c < 16 && j < sizeof(SECTOR_READ_END); c++) { 781 for (c = 0; c < 16 && j < sizeof(SECTOR_READ_END); c++) {
782 buf[i+c] = buffer[c]; 782 buf[i+c] = buffer[c];
783 if (buffer[c] == SECTOR_READ_END[j]) { 783 if (buffer[c] == SECTOR_READ_END[j]) {
784 j++; 784 j++;
785 } else if (buffer[c] == SECTOR_READ_END[0]) { 785 } else if (buffer[c] == SECTOR_READ_END[0]) {
786 j = 1; 786 j = 1;
787 } else { 787 } else {
788 j = 0; 788 j = 0;
789 } 789 }
790 } 790 }
791 } 791 }
792 if (j < sizeof(SECTOR_READ_END)) { 792 if (j < sizeof(SECTOR_READ_END)) {
793 db(1, MYNAME ": Didn't get sector end tag\n"); 793 db(1, MYNAME ": Didn't get sector end tag\n");
794 return res_ERROR; 794 return res_ERROR;
795 } 795 }
796 if (c < 16) { 796 if (c < 16) {
797 buf[i] = buffer[c]; 797 buf[i] = buffer[c];
798 } else { 798 } else {
799 c = rd_char(&errors); /* read checksum byte */ 799 c = rd_char(&errors); /* read checksum byte */
800 buf[i] = c; 800 buf[i] = c;
801 } 801 }
802 #endif 802 #endif
803 i = i-j; 803 i = i-j;
804 db(3, "Received %i bytes of log data\n", i); 804 db(3, "Received %i bytes of log data\n", i);
805 805
806 //#define SINGLE_READ_WORKAROUND 806 //#define SINGLE_READ_WORKAROUND
807 #ifdef SINGLE_READ_WORKAROUND 807 #ifdef SINGLE_READ_WORKAROUND
808 gbser_set_speed(serial_handle, skytraq_baud); 808 gbser_set_speed(serial_handle, skytraq_baud);
809 rd_char(&errors); 809 rd_char(&errors);
810 rd_char(&errors); 810 rd_char(&errors);
811 rd_char(&errors); 811 rd_char(&errors);
812 rd_char(&errors); 812 rd_char(&errors);
813 rd_char(&errors); 813 rd_char(&errors);
814 rd_char(&errors); 814 rd_char(&errors);
815 skytraq_set_baud(atoi(opt_dlbaud)); 815 skytraq_set_baud(atoi(opt_dlbaud));
816 #endif 816 #endif
817 817
818 cs = skytraq_calc_checksum(buf, i); 818 cs = skytraq_calc_checksum(buf, i);
819 if (cs != buf[i+sizeof(SECTOR_READ_END)]) { 819 if (cs != buf[i+sizeof(SECTOR_READ_END)]) {
820 db(1, MYNAME ": Checksum error while reading sector: got 0x%02x, expected 0x%02x\n", 820 db(1, MYNAME ": Checksum error while reading sector: got 0x%02x, expected 0x%02x\n",
821 buf[i+sizeof(SECTOR_READ_END)], cs); 821 buf[i+sizeof(SECTOR_READ_END)], cs);
822 return res_ERROR; 822 return res_ERROR;
823 } 823 }
824 824
825 for (; i < SECTOR_SIZE; i++) { 825 for (; i < SECTOR_SIZE; i++) {
826 buf[i] = 0xFF; 826 buf[i] = 0xFF;
827 } 827 }
828 828
829 return res_OK; 829 return res_OK;
830 } 830 }
831 831
832 static int 832 static int
833 skytraq_read_multiple_sectors(int first_sector, int sector_count, gbuint8 *buf) 833 skytraq_read_multiple_sectors(int first_sector, int sector_count, gbuint8 *buf)
834 { 834 {
835 gbuint8 MSG_LOG_READ_MULTI_SECTORS[5] = { 0x1D }; 835 gbuint8 MSG_LOG_READ_MULTI_SECTORS[5] = { 0x1D };
836 gbuint8 *buf_end_tag; 836 gbuint8 *buf_end_tag;
837 int cs, i, read_result; 837 int cs, i, read_result;
838 838
839 if (first_sector < 0 || first_sector > 0xFFFF) { 839 if (first_sector < 0 || first_sector > 0xFFFF) {
840 fatal(MYNAME ": Invalid sector number (%i)\n", first_sector); 840 fatal(MYNAME ": Invalid sector number (%i)\n", first_sector);
841 } 841 }
842 be_write16(&MSG_LOG_READ_MULTI_SECTORS[1], first_sector); 842 be_write16(&MSG_LOG_READ_MULTI_SECTORS[1], first_sector);
843 if (sector_count < 0 || sector_count > 0xFFFF) { 843 if (sector_count < 0 || sector_count > 0xFFFF) {
844 fatal(MYNAME ": Invalid sector count (%i)\n", sector_count); 844 fatal(MYNAME ": Invalid sector count (%i)\n", sector_count);
845 } 845 }
846 be_write16(&MSG_LOG_READ_MULTI_SECTORS[3], sector_count); 846 be_write16(&MSG_LOG_READ_MULTI_SECTORS[3], sector_count);
847 847
848 db(2, "Reading %i sectors beginning from #%i...\n", sector_count, first_sector); 848 db(2, "Reading %i sectors beginning from #%i...\n", sector_count, first_sector);
849 849
850 read_result = skytraq_wr_msg_verify((gbuint8*)&MSG_LOG_READ_MULTI_SECTORS, sizeof(MSG_LOG_READ_MULTI_SECTORS)); 850 read_result = skytraq_wr_msg_verify((gbuint8*)&MSG_LOG_READ_MULTI_SECTORS, sizeof(MSG_LOG_READ_MULTI_SECTORS));
851 if (read_result != res_OK) { 851 if (read_result != res_OK) {
852 return read_result; 852 return read_result;
853 } 853 }
854 854
855 for (i = 0; i < sector_count; i++) { 855 for (i = 0; i < sector_count; i++) {
856 db(2, "Receiving data of sector #%i...\n", first_sector+i); 856 db(2, "Receiving data of sector #%i...\n", first_sector+i);
857 rd_buf(buf+i*SECTOR_SIZE, SECTOR_SIZE); 857 rd_buf(buf+i*SECTOR_SIZE, SECTOR_SIZE);
858 } 858 }
859 rd_buf(buf+SECTOR_SIZE*sector_count, sizeof(SECTOR_READ_END)+6); 859 rd_buf(buf+SECTOR_SIZE*sector_count, sizeof(SECTOR_READ_END)+6);
860 860
861 buf_end_tag = buf + SECTOR_SIZE*sector_count; 861 buf_end_tag = buf + SECTOR_SIZE*sector_count;
862 for (i = 0; i < sizeof(SECTOR_READ_END); i++) { 862 for (i = 0; i < sizeof(SECTOR_READ_END); i++) {
863 if (buf_end_tag[i] != SECTOR_READ_END[i]) { 863 if (buf_end_tag[i] != SECTOR_READ_END[i]) {
864 db(1, MYNAME ": Wrong end tag: got 0x%02x ('%c'), expected 0x%02x ('%c')\n", 864 db(1, MYNAME ": Wrong end tag: got 0x%02x ('%c'), expected 0x%02x ('%c')\n",
865 buf_end_tag[i], isprint(buf_end_tag[i]) ? buf_end_tag[i] : '.', 865 buf_end_tag[i], isprint(buf_end_tag[i]) ? buf_end_tag[i] : '.',
866 SECTOR_READ_END[i], isprint(SECTOR_READ_END[i]) ? SECTOR_READ_END[i] : '.'); 866 SECTOR_READ_END[i], isprint(SECTOR_READ_END[i]) ? SECTOR_READ_END[i] : '.');
867 return res_ERROR; 867 return res_ERROR;
868 } 868 }
869 } 869 }
870 870
871 cs = skytraq_calc_checksum(buf, SECTOR_SIZE*sector_count); 871 cs = skytraq_calc_checksum(buf, SECTOR_SIZE*sector_count);
872 if (cs != buf_end_tag[sizeof(SECTOR_READ_END)]) { 872 if (cs != buf_end_tag[sizeof(SECTOR_READ_END)]) {
873 db(1, MYNAME ": Checksum error while reading sector: got 0x%02x, expected 0x%02x\n", 873 db(1, MYNAME ": Checksum error while reading sector: got 0x%02x, expected 0x%02x\n",
874 buf_end_tag[sizeof(SECTOR_READ_END)], cs); 874 buf_end_tag[sizeof(SECTOR_READ_END)], cs);
875 return res_ERROR; 875 return res_ERROR;
876 } 876 }
877 877
878 return res_OK; 878 return res_OK;
879 } 879 }
880 880
881 static void 881 static void
882 skytraq_read_tracks(void) 882 skytraq_read_tracks(void)
883 { 883 {
884 struct read_state st; 884 struct read_state st;
885 gbuint32 log_wr_ptr; 885 gbuint32 log_wr_ptr;
886 gbuint16 sectors_free, sectors_total, /*sectors_used_a, sectors_used_b,*/ sectors_used; 886 gbuint16 sectors_free, sectors_total, /*sectors_used_a, sectors_used_b,*/ sectors_used;
887 int i, t, s, rc, got_sectors, total_sectors_read = 0; 887 int i, t, s, rc, got_sectors, total_sectors_read = 0;
888 int read_at_once = MAX(atoi(opt_read_at_once), 1); 888 int read_at_once = MAX(atoi(opt_read_at_once), 1);
889 int opt_first_sector_val = atoi(opt_first_sector); 889 int opt_first_sector_val = atoi(opt_first_sector);
890 int opt_last_sector_val = atoi(opt_last_sector); 890 int opt_last_sector_val = atoi(opt_last_sector);
891 int multi_read_supported = 1; 891 int multi_read_supported = 1;
892 gbuint8 *buffer = NULL; 892 gbuint8 *buffer = NULL;
893 gbfile *dumpfile = NULL; 893 gbfile *dumpfile = NULL;
894 894
895 state_init(&st); 895 state_init(&st);
896 896
897 if (skytraq_get_log_buffer_status(&log_wr_ptr, &sectors_free, &sectors_total) != res_OK) { 897 if (skytraq_get_log_buffer_status(&log_wr_ptr, &sectors_free, &sectors_total) != res_OK) {
898 fatal(MYNAME ": Can't get log buffer status\n"); 898 fatal(MYNAME ": Can't get log buffer status\n");
899 } 899 }
900 900
901 db(1, MYNAME ": Device status: free sectors: %i / total sectors: %i / %i%% used / write ptr: %i\n", 901 db(1, MYNAME ": Device status: free sectors: %i / total sectors: %i / %i%% used / write ptr: %i\n",
902 sectors_free, sectors_total, 100 - sectors_free*100 / sectors_total, log_wr_ptr); 902 sectors_free, sectors_total, 100 - sectors_free*100 / sectors_total, log_wr_ptr);
903 903
904 if (opt_first_sector_val >= sectors_total) { 904 if (opt_first_sector_val >= sectors_total) {
905 db(1, "Warning: sector# specified by option first-sector (%i) is beyond reported total sector count (%i)", 905 db(1, "Warning: sector# specified by option first-sector (%i) is beyond reported total sector count (%i)",
906 opt_first_sector_val, sectors_total); 906 opt_first_sector_val, sectors_total);
907 } 907 }
908 /* Workaround: sectors_free is sometimes reported wrong. Tried to use log_wr_ptr as an 908 /* Workaround: sectors_free is sometimes reported wrong. Tried to use log_wr_ptr as an
909 indicator for how many sectors are currently used. However this isn't correct in every case too. 909 indicator for how many sectors are currently used. However this isn't correct in every case too.
910 The current read logic is aware of that so this shouldn't be necessary anymore. 910 The current read logic is aware of that so this shouldn't be necessary anymore.
911 sectors_used_a = sectors_total - sectors_free; 911 sectors_used_a = sectors_total - sectors_free;
912 sectors_used_b = (log_wr_ptr + SECTOR_SIZE - 1) / SECTOR_SIZE; 912 sectors_used_b = (log_wr_ptr + SECTOR_SIZE - 1) / SECTOR_SIZE;
913 if (sectors_used_a != sectors_used_b) { 913 if (sectors_used_a != sectors_used_b) {
914 db(1, "Warning: device reported inconsistent number of used sectors (a=%i, b=%i), "\ 914 db(1, "Warning: device reported inconsistent number of used sectors (a=%i, b=%i), "\
915 "using max=%i\n", sectors_used_a, sectors_used_b, MAX(sectors_used_a, sectors_used_b)); 915 "using max=%i\n", sectors_used_a, sectors_used_b, MAX(sectors_used_a, sectors_used_b));
916 } 916 }
917 sectors_used = MAX(sectors_used_a, sectors_used_b); 917 sectors_used = MAX(sectors_used_a, sectors_used_b);
918 */ 918 */
919 if (opt_last_sector_val < 0) { 919 if (opt_last_sector_val < 0) {
920 sectors_used = sectors_total - sectors_free + 1 /*+5*/; 920 sectors_used = sectors_total - sectors_free + 1 /*+5*/;
921 if (opt_first_sector_val >= sectors_used) { 921 if (opt_first_sector_val >= sectors_used) {
922 sectors_used = opt_first_sector_val + 1; 922 sectors_used = opt_first_sector_val + 1;
923 } 923 }
924 } else { 924 } else {
925 sectors_used = opt_last_sector_val; 925 sectors_used = opt_last_sector_val;
926 if (opt_last_sector_val >= sectors_total) { 926 if (opt_last_sector_val >= sectors_total) {
927 db(1, "Warning: sector# specified by option last-sector (%i) is beyond reported total sector count (%i)", 927 db(1, "Warning: sector# specified by option last-sector (%i) is beyond reported total sector count (%i)",
928 opt_last_sector_val, sectors_total); 928 opt_last_sector_val, sectors_total);
929 } 929 }
930 } 930 }
931 931
932 buffer = (gbuint8*) xmalloc(SECTOR_SIZE*read_at_once+sizeof(SECTOR_READ_END)+6); 932 buffer = (gbuint8*) xmalloc(SECTOR_SIZE*read_at_once+sizeof(SECTOR_READ_END)+6);
933 // m.ad/090930: removed code that tried reducing read_at_once if necessary since doesn't work with xmalloc 933 // m.ad/090930: removed code that tried reducing read_at_once if necessary since doesn't work with xmalloc
934 934
935 if (opt_dump_file) { 935 if (opt_dump_file) {
936 dumpfile = gbfopen(opt_dump_file, "w", MYNAME); 936 dumpfile = gbfopen(opt_dump_file, "w", MYNAME);
937 } 937 }
938 938
939 db(1, MYNAME ": Reading log data from device...\n"); 939 db(1, MYNAME ": Reading log data from device...\n");
940 db(1, MYNAME ": start=%d used=%d\n", opt_first_sector_val, sectors_used); 940 db(1, MYNAME ": start=%d used=%d\n", opt_first_sector_val, sectors_used);
941 db(1, MYNAME ": opt_last_sector_val=%d\n", opt_last_sector_val); 941 db(1, MYNAME ": opt_last_sector_val=%d\n", opt_last_sector_val);
942 for (i = opt_first_sector_val; i < sectors_used; i += got_sectors) { 942 for (i = opt_first_sector_val; i < sectors_used; i += got_sectors) {
943 for (t = 0, got_sectors = 0; (t < SECTOR_RETRIES) && (got_sectors <= 0); t++) { 943 for (t = 0, got_sectors = 0; (t < SECTOR_RETRIES) && (got_sectors <= 0); t++) {
944 if (atoi(opt_read_at_once) == 0 || multi_read_supported == 0) { 944 if (atoi(opt_read_at_once) == 0 || multi_read_supported == 0) {
945 rc = skytraq_read_single_sector(i, buffer); 945 rc = skytraq_read_single_sector(i, buffer);
946 if (rc == res_OK) { 946 if (rc == res_OK) {
947 got_sectors = 1; 947 got_sectors = 1;
948 } 948 }
949 } else { 949 } else {
950 /* Try to read read_at_once sectors at once. 950 /* Try to read read_at_once sectors at once.
951 * If tere aren't any so many interesting ones, read the remainder (sectors_used-i). 951 * If tere aren't any so many interesting ones, read the remainder (sectors_used-i).
952 * And read at least 1 sector. 952 * And read at least 1 sector.
953 */ 953 */
954 read_at_once = MAX(MIN(read_at_once, sectors_used-i), 1); 954 read_at_once = MAX(MIN(read_at_once, sectors_used-i), 1);
955 955
956 rc = skytraq_read_multiple_sectors(i, read_at_once, buffer); 956 rc = skytraq_read_multiple_sectors(i, read_at_once, buffer);
957 switch (rc) { 957 switch (rc) {
958 case res_OK: 958 case res_OK:
959 got_sectors = read_at_once; 959 got_sectors = read_at_once;
960 read_at_once = MIN(read_at_once*2, atoi(opt_read_at_once)); 960 read_at_once = MIN(read_at_once*2, atoi(opt_read_at_once));
961 break; 961 break;
962 962
963 case res_NACK: 963 case res_NACK:
964 db(1, MYNAME ": Device doesn't seem to support reading multiple " 964 db(1, MYNAME ": Device doesn't seem to support reading multiple "
965 "sectors at once, falling back to single read.\n"); 965 "sectors at once, falling back to single read.\n");
966 multi_read_supported = 0; 966 multi_read_supported = 0;
967 break; 967 break;
968 968
969 default: 969 default:
970 /* On failure, try with less sectors */ 970 /* On failure, try with less sectors */
971 read_at_once = MAX(read_at_once/2, 1); 971 read_at_once = MAX(read_at_once/2, 1);
972 } 972 }
973 } 973 }
974 } 974 }
975 if (got_sectors <= 0) { 975 if (got_sectors <= 0) {
976 fatal(MYNAME ": Error reading sector %i\n", i); 976 fatal(MYNAME ": Error reading sector %i\n", i);
977 } 977 }
978 978
979 total_sectors_read += got_sectors; 979 total_sectors_read += got_sectors;
980 980
981 if (dumpfile) { 981 if (dumpfile) {
982 gbfwrite(buffer, SECTOR_SIZE, got_sectors, dumpfile); 982 gbfwrite(buffer, SECTOR_SIZE, got_sectors, dumpfile);
983 } 983 }
984 984
985 if (*opt_no_output == '1') { 985 if (*opt_no_output == '1') {
986 continue; // skip decoding 986 continue; // skip decoding
987 } 987 }
988 988
989 for (s = 0; s < got_sectors; s++) { 989 for (s = 0; s < got_sectors; s++) {
990 db(4, MYNAME ": Decoding sector #%i...\n", i+s); 990 db(4, MYNAME ": Decoding sector #%i...\n", i+s);
991 rc = process_data_sector(&st, buffer+s*SECTOR_SIZE, SECTOR_SIZE); 991 rc = process_data_sector(&st, buffer+s*SECTOR_SIZE, SECTOR_SIZE);
992 if (rc == 0) { 992 if (rc == 0) {
993 db(1, MYNAME ": Empty sector encountered: apparently only %i sectors are " 993 db(1, MYNAME ": Empty sector encountered: apparently only %i sectors are "
994 "used but device reported %i.\n", 994 "used but device reported %i.\n",
995 i+s, sectors_used); 995 i+s, sectors_used);
996 i = sectors_used; /* terminate to avoid reading stale data still in the logger */ 996 i = sectors_used; /* terminate to avoid reading stale data still in the logger */
997 break; 997 break;
998 } else if (rc >= (4096-FULL_ITEM_LEN) && i+s+1 >= sectors_used && i+s+1 < sectors_total) { 998 } else if (rc >= (4096-FULL_ITEM_LEN) && i+s+1 >= sectors_used && i+s+1 < sectors_total) {
999 db(1, MYNAME ": Last sector is nearly full, reading one more sector\n"); 999 db(1, MYNAME ": Last sector is nearly full, reading one more sector\n");
1000 sectors_used++; 1000 sectors_used++;
1001 } 1001 }
1002 } 1002 }
1003 } 1003 }
1004 free(buffer); 1004 free(buffer);
1005 db(1, MYNAME ": Got %i trackpoints from %i sectors.\n", st.tpn, total_sectors_read); 1005 db(1, MYNAME ": Got %i trackpoints from %i sectors.\n", st.tpn, total_sectors_read);
1006 1006
1007 if (dumpfile) { 1007 if (dumpfile) {
1008 gbfclose(dumpfile); 1008 gbfclose(dumpfile);
1009 } 1009 }
1010 } 1010 }
1011 1011
1012 static int 1012 static int
1013 skytraq_probe(void) 1013 skytraq_probe(void)
1014 { 1014 {
1015 int baud_rates[] = { 9600, 230400, 115200, 57600, 4800, 19200, 38400 }; 1015 int baud_rates[] = { 9600, 230400, 115200, 57600, 4800, 19200, 38400 };
1016 int baud_rates_count = sizeof(baud_rates)/sizeof(baud_rates[0]); 1016 int baud_rates_count = sizeof(baud_rates)/sizeof(baud_rates[0]);
1017 int initbaud = atoi(opt_initbaud); 1017 int initbaud = atoi(opt_initbaud);
1018 gbuint8 MSG_QUERY_SOFTWARE_VERSION[2] = { 0x02, 0x01 }; 1018 gbuint8 MSG_QUERY_SOFTWARE_VERSION[2] = { 0x02, 0x01 };
1019 struct { 1019 struct {
1020 gbuint8 id; 1020 gbuint8 id;
1021 gbuint8 sw_type; 1021 gbuint8 sw_type;
1022 gbuint8 kernel_ver[4]; 1022 gbuint8 kernel_ver[4];
1023 gbuint8 odm_ver[4]; 1023 gbuint8 odm_ver[4];
1024 gbuint8 revision[4]; 1024 gbuint8 revision[4];
1025 } MSG_SOFTWARE_VERSION; 1025 } MSG_SOFTWARE_VERSION;
1026 int i, rc; 1026 int i, rc;
1027 1027
1028 // TODO: get current serial port baud rate and try that first 1028 // TODO: get current serial port baud rate and try that first
1029 // (only sensible if init to 4800 can be disabled...) 1029 // (only sensible if init to 4800 can be disabled...)
1030 1030
1031 if (initbaud > 0) { 1031 if (initbaud > 0) {
1032 baud_rates[0] = initbaud; 1032 baud_rates[0] = initbaud;
1033 baud_rates_count = 1; 1033 baud_rates_count = 1;
1034 } 1034 }
1035 1035
1036 for (i = 0; i < baud_rates_count; i++) { 1036 for (i = 0; i < baud_rates_count; i++) {
1037 db(1, MYNAME ": Probing SkyTraq Venus at %ibaud...\n", baud_rates[i]); 1037 db(1, MYNAME ": Probing SkyTraq Venus at %ibaud...\n", baud_rates[i]);
1038 1038
1039 rd_drain(); 1039 rd_drain();
1040 if ((rc = gbser_set_speed(serial_handle, baud_rates[i])) != gbser_OK) { 1040 if ((rc = gbser_set_speed(serial_handle, baud_rates[i])) != gbser_OK) {
1041 db(1, MYNAME ": Set baud rate to %d failed (%d), retrying...\n", baud_rates[i], rc); 1041 db(1, MYNAME ": Set baud rate to %d failed (%d), retrying...\n", baud_rates[i], rc);
1042 if ((rc = gbser_set_speed(serial_handle, baud_rates[i])) != gbser_OK) { 1042 if ((rc = gbser_set_speed(serial_handle, baud_rates[i])) != gbser_OK) {
1043 db(1, MYNAME ": Set baud rate to %d failed (%d)\n", baud_rates[i], rc); 1043 db(1, MYNAME ": Set baud rate to %d failed (%d)\n", baud_rates[i], rc);
1044 continue; 1044 continue;
1045 } 1045 }
1046 } 1046 }
1047 1047
1048 gb_sleep(50); /* allow UART to settle. */ 1048 gb_sleep(50); /* allow UART to settle. */
1049 1049
1050 skytraq_wr_msg(MSG_QUERY_SOFTWARE_VERSION, /* get firmware version */ 1050 skytraq_wr_msg(MSG_QUERY_SOFTWARE_VERSION, /* get firmware version */
1051 sizeof(MSG_QUERY_SOFTWARE_VERSION)); 1051 sizeof(MSG_QUERY_SOFTWARE_VERSION));
1052 if ((rc = skytraq_expect_ack(0x02)) != res_OK) { 1052 if ((rc = skytraq_expect_ack(0x02)) != res_OK) {
1053 db(2, "Didn't receive ACK (%d), retrying...\n", rc); 1053 db(2, "Didn't receive ACK (%d), retrying...\n", rc);
1054 skytraq_wr_msg(MSG_QUERY_SOFTWARE_VERSION, /* get firmware version */ 1054 skytraq_wr_msg(MSG_QUERY_SOFTWARE_VERSION, /* get firmware version */
1055 sizeof(MSG_QUERY_SOFTWARE_VERSION)); 1055 sizeof(MSG_QUERY_SOFTWARE_VERSION));
1056 if ((rc = skytraq_expect_ack(0x02)) != res_OK) { 1056 if ((rc = skytraq_expect_ack(0x02)) != res_OK) {
1057 db(2, "Didn't receive ACK (%d)\n", rc); 1057 db(2, "Didn't receive ACK (%d)\n", rc);
1058 continue; 1058 continue;
1059 } 1059 }
1060 } 1060 }
1061 /* note: _verify retries on errors, probe takes too long. 1061 /* note: _verify retries on errors, probe takes too long.
1062 if (skytraq_wr_msg_verify(MSG_QUERY_SOFTWARE_VERSION, 1062 if (skytraq_wr_msg_verify(MSG_QUERY_SOFTWARE_VERSION,
1063 sizeof(MSG_QUERY_SOFTWARE_VERSION)) != res_OK) 1063 sizeof(MSG_QUERY_SOFTWARE_VERSION)) != res_OK)
1064 { 1064 {
1065 continue; 1065 continue;
1066 }*/ 1066 }*/
1067 rc = skytraq_expect_msg(0x80, (gbuint8*)&MSG_SOFTWARE_VERSION, sizeof(MSG_SOFTWARE_VERSION)); 1067 rc = skytraq_expect_msg(0x80, (gbuint8*)&MSG_SOFTWARE_VERSION, sizeof(MSG_SOFTWARE_VERSION));
1068 if (rc < (int)sizeof(MSG_SOFTWARE_VERSION)) { 1068 if (rc < (int)sizeof(MSG_SOFTWARE_VERSION)) {
1069 db(2, "Didn't receive expected reply (%d)\n", rc); 1069 db(2, "Didn't receive expected reply (%d)\n", rc);
1070 } else { 1070 } else {
1071 db(1, MYNAME ": Venus device found: Kernel version = %i.%i.%i, ODM version = %i.%i.%i, "\ 1071 db(1, MYNAME ": Venus device found: Kernel version = %i.%i.%i, ODM version = %i.%i.%i, "\
1072 "revision (Y/M/D) = %02i/%02i/%02i\n", 1072 "revision (Y/M/D) = %02i/%02i/%02i\n",
1073 MSG_SOFTWARE_VERSION.kernel_ver[1], MSG_SOFTWARE_VERSION.kernel_ver[2], 1073 MSG_SOFTWARE_VERSION.kernel_ver[1], MSG_SOFTWARE_VERSION.kernel_ver[2],
1074 MSG_SOFTWARE_VERSION.kernel_ver[3], 1074 MSG_SOFTWARE_VERSION.kernel_ver[3],
1075 MSG_SOFTWARE_VERSION.odm_ver[1], MSG_SOFTWARE_VERSION.odm_ver[2], 1075 MSG_SOFTWARE_VERSION.odm_ver[1], MSG_SOFTWARE_VERSION.odm_ver[2],
1076 MSG_SOFTWARE_VERSION.odm_ver[3], 1076 MSG_SOFTWARE_VERSION.odm_ver[3],
1077 MSG_SOFTWARE_VERSION.revision[1], MSG_SOFTWARE_VERSION.revision[2], 1077 MSG_SOFTWARE_VERSION.revision[1], MSG_SOFTWARE_VERSION.revision[2],
1078 MSG_SOFTWARE_VERSION.revision[3]); 1078 MSG_SOFTWARE_VERSION.revision[3]);
1079 1079
1080 return baud_rates[i]; 1080 return baud_rates[i];
1081 } 1081 }
1082 } 1082 }
1083 1083
1084 return res_NOTFOUND; 1084 return res_NOTFOUND;
1085 } 1085 }
1086 1086
1087 static int 1087 static int
1088 skytraq_erase() 1088 skytraq_erase()
1089 { 1089 {
1090 gbuint8 MSG_LOG_ERASE = 0x19; 1090 gbuint8 MSG_LOG_ERASE = 0x19;
1091 1091
1092 db(1, MYNAME ": Erasing logger memory...\n"); 1092 db(1, MYNAME ": Erasing logger memory...\n");
1093 if (skytraq_wr_msg_verify(&MSG_LOG_ERASE, sizeof(MSG_LOG_ERASE)) != res_OK) { 1093 if (skytraq_wr_msg_verify(&MSG_LOG_ERASE, sizeof(MSG_LOG_ERASE)) != res_OK) {
1094 db(1, MYNAME ": Didn't receive ACK\n"); 1094 db(1, MYNAME ": Didn't receive ACK\n");
1095 return res_ERROR; 1095 return res_ERROR;
1096 } 1096 }
1097 1097
1098 return res_OK; 1098 return res_OK;
1099 } 1099 }
1100 1100
1101 static void 1101 static void
1102 skytraq_set_location(void) 1102 skytraq_set_location(void)
1103 { 1103 {
1104 double lat, lng; 1104 double lat, lng;
1105 int i; 1105 int i;
1106 gbuint8 MSG_SET_LOCATION[17] = { 0x36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 1106 gbuint8 MSG_SET_LOCATION[17] = { 0x36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1107 gbuint8 MSG_GET_LOCATION = 0x35; 1107 gbuint8 MSG_GET_LOCATION = 0x35;
1108 1108
1109 db(3, MYNAME ": set_location='%s'\n", opt_set_location); 1109 db(3, MYNAME ": set_location='%s'\n", opt_set_location);
1110 1110
1111 sscanf(opt_set_location, "%lf:%lf", &lat, &lng); 1111 sscanf(opt_set_location, "%lf:%lf", &lat, &lng);
1112 le_write_double(&MSG_SET_LOCATION[1], lat); 1112 le_write_double(&MSG_SET_LOCATION[1], lat);
1113 le_write_double(&MSG_SET_LOCATION[9], lng); 1113 le_write_double(&MSG_SET_LOCATION[9], lng);
1114 for (i=0; i<sizeof MSG_SET_LOCATION; i++) { 1114 for (i=0; i<sizeof MSG_SET_LOCATION; i++) {
1115 db(3, "%02x ", MSG_SET_LOCATION[i]); 1115 db(3, "%02x ", MSG_SET_LOCATION[i]);
1116 } 1116 }
1117 db(3, "\n"); 1117 db(3, "\n");
1118 if (skytraq_wr_msg_verify((gbuint8*)&MSG_SET_LOCATION, sizeof(MSG_SET_LOCATION)) != res_OK) { 1118 if (skytraq_wr_msg_verify((gbuint8*)&MSG_SET_LOCATION, sizeof(MSG_SET_LOCATION)) != res_OK) {
1119 fatal(MYNAME ": cannot set new location\n"); 1119 fatal(MYNAME ": cannot set new location\n");
1120 } 1120 }
1121 { 1121 {
1122 char buf[32]; 1122 char buf[32];
1123 skytraq_wr_msg_verify(&MSG_GET_LOCATION, 1); 1123 skytraq_wr_msg_verify(&MSG_GET_LOCATION, 1);
1124 skytraq_rd_msg(buf, 32); 1124 skytraq_rd_msg(buf, 32);
1125 } 1125 }
1126 } 1126 }
1127 1127
1128 /******************************************************************************* 1128 /*******************************************************************************
1129 * %%% global callbacks called by gpsbabel main process %%% * 1129 * %%% global callbacks called by gpsbabel main process %%% *
1130 *******************************************************************************/ 1130 *******************************************************************************/
1131 1131
1132 static void 1132 static void
1133 skytraq_rd_init(const char *fname) 1133 skytraq_rd_init(const char *fname)
1134 { 1134 {
1135 port = xstrdup(fname); 1135 port = xstrdup(fname);
1136 if ((serial_handle = gbser_init(fname)) == NULL) { 1136 if ((serial_handle = gbser_init(fname)) == NULL) {
1137 fatal(MYNAME ": Can't open port '%s'\n", fname); 1137 fatal(MYNAME ": Can't open port '%s'\n", fname);
1138 } 1138 }
1139 if ((skytraq_baud = skytraq_probe()) <= 0) { 1139 if ((skytraq_baud = skytraq_probe()) <= 0) {
1140 fatal(MYNAME ": Can't find skytraq device on '%s'\n", fname); 1140 fatal(MYNAME ": Can't find skytraq device on '%s'\n", fname);
1141 } 1141 }
1142 } 1142 }
1143 1143
1144 static void 1144 static void
1145 skytraq_rd_deinit(void) 1145 skytraq_rd_deinit(void)
1146 { 1146 {
1147 gbser_deinit(serial_handle); 1147 gbser_deinit(serial_handle);
1148 serial_handle = NULL; 1148 serial_handle = NULL;
1149 xfree(port); 1149 xfree(port);
1150 } 1150 }
1151 1151
1152 static void 1152 static void
1153 skytraq_read(void) 1153 skytraq_read(void)
1154 { 1154 {
1155 int dlbaud; 1155 int dlbaud;
1156 1156
1157 if (*opt_set_location) { 1157 if (*opt_set_location) {
1158 skytraq_set_location(); 1158 skytraq_set_location();
1159 return; 1159 return;
1160 } 1160 }
1161 1161
1162 dlbaud = atoi(opt_dlbaud); 1162 dlbaud = atoi(opt_dlbaud);
1163 if (dlbaud != 0 && dlbaud != skytraq_baud) { 1163 if (dlbaud != 0 && dlbaud != skytraq_baud) {
1164 skytraq_set_baud(dlbaud); 1164 skytraq_set_baud(dlbaud);
1165 } 1165 }
1166 1166
1167 // read device unless no-output=1 and dump-file=0 (i.e. no data needed at all) 1167 // read device unless no-output=1 and dump-file=0 (i.e. no data needed at all)
1168 if (*opt_no_output == '0' || opt_dump_file != NULL) { 1168 if (*opt_no_output == '0' || opt_dump_file != NULL) {
1169 skytraq_read_tracks(); 1169 skytraq_read_tracks();
1170 } 1170 }
1171 1171
1172 if (*opt_erase == '1') { 1172 if (*opt_erase == '1') {
1173 skytraq_erase(); 1173 skytraq_erase();
1174 } 1174 }
1175 1175
1176 if (dlbaud != 0 && dlbaud != skytraq_baud) { 1176 if (dlbaud != 0 && dlbaud != skytraq_baud) {
1177 skytraq_set_baud(skytraq_baud); // note that _system_restart resets baud rate anyway... 1177 skytraq_set_baud(skytraq_baud); // note that _system_restart resets baud rate anyway...
1178 } 1178 }
1179 skytraq_system_restart(); 1179 skytraq_system_restart();
1180 } 1180 }
1181 1181
1182 static void 1182 static void
1183 file_init(const char *fname) 1183 file_init(const char *fname)
1184 { 1184 {
1185 db(1, "Opening file...\n"); 1185 db(1, "Opening file...\n");
1186 if ((file_handle = gbfopen(fname, "rb", MYNAME)) == NULL) { 1186 if ((file_handle = gbfopen(fname, "rb", MYNAME)) == NULL) {
1187 fatal(MYNAME ": Can't open file '%s'\n", fname); 1187 fatal(MYNAME ": Can't open file '%s'\n", fname);
1188 } 1188 }
1189 } 1189 }
1190 1190
1191 static void 1191 static void
1192 file_deinit(void) 1192 file_deinit(void)
1193 { 1193 {
1194 db(1, "Closing file...\n"); 1194 db(1, "Closing file...\n");
1195 gbfclose(file_handle); 1195 gbfclose(file_handle);
1196 file_handle = NULL; 1196 file_handle = NULL;
1197 } 1197 }
1198 1198
1199 static void 1199 static void
1200 file_read(void) 1200 file_read(void)
1201 { 1201 {
1202 struct read_state st; 1202 struct read_state st;
1203 int rc, got_bytes; 1203 int rc, got_bytes;
1204 int opt_first_sector_val = atoi(opt_first_sector); 1204 int opt_first_sector_val = atoi(opt_first_sector);
1205 int opt_last_sector_val = atoi(opt_last_sector); 1205 int opt_last_sector_val = atoi(opt_last_sector);
1206 int sectors_read; 1206 int sectors_read;
1207 gbuint8 *buffer; 1207 gbuint8 *buffer;
1208 1208
1209 state_init(&st); 1209 state_init(&st);
1210 buffer = (gbuint8*) xmalloc(SECTOR_SIZE); 1210 buffer = (gbuint8*) xmalloc(SECTOR_SIZE);
1211 1211
1212 if (opt_first_sector_val > 0) { 1212 if (opt_first_sector_val > 0) {
1213 db(4, MYNAME ": Seeking to first-sector index %i\n", opt_first_sector_val*SECTOR_SIZE); 1213 db(4, MYNAME ": Seeking to first-sector index %i\n", opt_first_sector_val*SECTOR_SIZE);
1214 gbfseek(file_handle, opt_first_sector_val*SECTOR_SIZE, SEEK_SET); 1214 gbfseek(file_handle, opt_first_sector_val*SECTOR_SIZE, SEEK_SET);
1215 } 1215 }
1216 1216
1217 db(1, MYNAME ": Reading log data from file...\n"); 1217 db(1, MYNAME ": Reading log data from file...\n");
1218 sectors_read = 0; 1218 sectors_read = 0;
1219 while ((got_bytes = gbfread(buffer, 1, SECTOR_SIZE, file_handle)) > 0) { 1219 while ((got_bytes = gbfread(buffer, 1, SECTOR_SIZE, file_handle)) > 0) {
1220 db(4, MYNAME ": Decoding sector #%i...\n", sectors_read++); 1220 db(4, MYNAME ": Decoding sector #%i...\n", sectors_read++);
1221 rc = process_data_sector(&st, buffer, got_bytes); 1221 rc = process_data_sector(&st, buffer, got_bytes);
1222 if (opt_last_sector_val < 0) { 1222 if (opt_last_sector_val < 0) {
1223 if (rc < (4096-FULL_ITEM_LEN)) { 1223 if (rc < (4096-FULL_ITEM_LEN)) {
1224 db(1, MYNAME ": Empty sector encountered, terminating.\n"); 1224 db(1, MYNAME ": Empty sector encountered, terminating.\n");
1225 break; 1225 break;
1226 } 1226 }
1227 } else if (sectors_read-1 >= opt_last_sector_val) { 1227 } else if (sectors_read-1 >= opt_last_sector_val) {
1228 db(1, MYNAME ": desired last-sector #%i reached, terminating.\n", sectors_read-1); 1228 db(1, MYNAME ": desired last-sector #%i reached, terminating.\n", sectors_read-1);
1229 break; 1229 break;
1230 } 1230 }
1231 } 1231 }
1232 xfree(buffer); 1232 xfree(buffer);
1233 db(1, MYNAME ": Got %i trackpoints from %i sectors.\n", st.tpn, sectors_read); 1233 db(1, MYNAME ": Got %i trackpoints from %i sectors.\n", st.tpn, sectors_read);
1234 } 1234 }
1235 1235
1236 /**************************************************************************/ 1236 /**************************************************************************/
1237 1237
1238 // capabilities below means: we can only read tracks 1238 // capabilities below means: we can only read tracks
1239 1239
1240 ff_vecs_t skytraq_vecs = { 1240 ff_vecs_t skytraq_vecs = {
1241 ff_type_serial, 1241 ff_type_serial,
1242 { 1242 {
1243 ff_cap_read /* waypoints */, 1243 ff_cap_read /* waypoints */,
1244 ff_cap_read /* tracks */, 1244 ff_cap_read /* tracks */,
1245 ff_cap_none /* routes */ 1245 ff_cap_none /* routes */
1246 }, 1246 },
1247 skytraq_rd_init, 1247 skytraq_rd_init,
1248 NULL, 1248 NULL,
1249 skytraq_rd_deinit, 1249 skytraq_rd_deinit,
1250 NULL, 1250 NULL,
1251 skytraq_read, 1251 skytraq_read,
1252 NULL, 1252 NULL,
1253 NULL, 1253 NULL,
1254 skytraq_args, 1254 skytraq_args,
1255 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */ 1255 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */
1256 }; 1256 };
1257 1257
1258 ff_vecs_t skytraq_fvecs = { 1258 ff_vecs_t skytraq_fvecs = {
1259 ff_type_file, 1259 ff_type_file,
1260 { 1260 {
1261 ff_cap_read /* waypoints */, 1261 ff_cap_read /* waypoints */,
1262 ff_cap_read /* tracks */, 1262 ff_cap_read /* tracks */,
1263 ff_cap_none /* routes */ 1263 ff_cap_none /* routes */
1264 }, 1264 },
1265 file_init, 1265 file_init,
1266 NULL, 1266 NULL,
1267 file_deinit, 1267 file_deinit,
1268 NULL, 1268 NULL,
1269 file_read, 1269 file_read,
1270 NULL, 1270 NULL,
1271 NULL, 1271 NULL,
1272 skytraq_fargs, 1272 skytraq_fargs,
1273 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */ 1273 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */
1274 }; 1274 };
1275 /**************************************************************************/ 1275 /**************************************************************************/
1276 /* 1276 /*
1277 * support POI of skytraq based miniHomer device 1277 * support POI of skytraq based miniHomer device
1278 * http://navin.com.tw/miniHomer.htm 1278 * http://navin.com.tw/miniHomer.htm
1279 * 2010-10-23 Josef Reisinger 1279 * 2010-10-23 Josef Reisinger
1280 */ 1280 */
1281 #ifdef MYNAME 1281 #ifdef MYNAME
1282 #undef MYNAME 1282 #undef MYNAME
1283 #endif 1283 #endif
1284 #define MYNAME "miniHomer" 1284 #define MYNAME "miniHomer"
1285 static char *opt_set_poi_home = NULL; /* set if a "poi" option was used */ 1285 static char *opt_set_poi_home = NULL; /* set if a "poi" option was used */
1286 static char *opt_set_poi_car = NULL; /* set if a "poi" option was used */ 1286 static char *opt_set_poi_car = NULL; /* set if a "poi" option was used */
1287 static char *opt_set_poi_boat = NULL; /* set if a "poi" option was used */ 1287 static char *opt_set_poi_boat = NULL; /* set if a "poi" option was used */
1288 static char *opt_set_poi_heart = NULL; /* set if a "poi" option was used */ 1288 static char *opt_set_poi_heart = NULL; /* set if a "poi" option was used */
1289 static char *opt_set_poi_bar = NULL; /* set if a "poi" option was used */ 1289 static char *opt_set_poi_bar = NULL; /* set if a "poi" option was used */
1290 arglist_t miniHomer_args[] = { 1290 arglist_t miniHomer_args[] = {
1291 { "baud", &opt_dlbaud, "Baud rate used for download", "115200", ARGTYPE_INT, "0", "115200" }, 1291 { "baud", &opt_dlbaud, "Baud rate used for download", "115200", ARGTYPE_INT, "0", "115200" },
1292 { "dump-file", &opt_dump_file, "Dump raw data to this file", NULL, ARGTYPE_OUTFILE, ARG_NOMINMAX }, 1292 { "dump-file", &opt_dump_file, "Dump raw data to this file", NULL, ARGTYPE_OUTFILE, ARG_NOMINMAX },
1293 { "erase", &opt_erase, "Erase device data after download", "0", ARGTYPE_BOOL, ARG_NOMINMAX }, 1293 { "erase", &opt_erase, "Erase device data after download", "0", ARGTYPE_BOOL, ARG_NOMINMAX },
1294 { "first-sector", &opt_first_sector, "First sector to be read from the device", "0", ARGTYPE_INT, "0", "65535" }, 1294 { "first-sector", &opt_first_sector, "First sector to be read from the device", "0", ARGTYPE_INT, "0", "65535" },
1295 { "initbaud", &opt_initbaud, "Baud rate used to init device (0=autodetect)", "38400", ARGTYPE_INT, "38400", "38400" }, 1295 { "initbaud", &opt_initbaud, "Baud rate used to init device (0=autodetect)", "38400", ARGTYPE_INT, "38400", "38400" },
1296 { "last-sector", &opt_last_sector, "Last sector to be read from the device (-1: smart read everything)", "-1", ARGTYPE_INT, "-1", "65535" }, 1296 { "last-sector", &opt_last_sector, "Last sector to be read from the device (-1: smart read everything)", "-1", ARGTYPE_INT, "-1", "65535" },
1297 { "no-output", &opt_no_output, "Disable output (useful with erase)", "0", ARGTYPE_BOOL, ARG_NOMINMAX }, 1297 { "no-output", &opt_no_output, "Disable output (useful with erase)", "0", ARGTYPE_BOOL, ARG_NOMINMAX },
1298 { "read-at-once", &opt_read_at_once, "Number of sectors to read at once (0=use single sector mode)", "255", ARGTYPE_INT, "0", "255" }, 1298 { "read-at-once", &opt_read_at_once, "Number of sectors to read at once (0=use single sector mode)", "255", ARGTYPE_INT, "0", "255" },
1299 { "Home", &opt_set_poi_home, "POI for Home Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" }, 1299 { "Home", &opt_set_poi_home, "POI for Home Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" },
1300 { "Car", &opt_set_poi_car, "POI for Car Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" }, 1300 { "Car", &opt_set_poi_car, "POI for Car Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" },
1301 { "Boat", &opt_set_poi_boat, "POI for Boat Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" }, 1301 { "Boat", &opt_set_poi_boat, "POI for Boat Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" },
1302 { "Heart", &opt_set_poi_heart, "POI for Heart Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" }, 1302 { "Heart", &opt_set_poi_heart, "POI for Heart Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" },
1303 { "Bar", &opt_set_poi_bar, "POI for Bar Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" }, 1303 { "Bar", &opt_set_poi_bar, "POI for Bar Symbol as lat:lng[:alt]", NULL, ARGTYPE_STRING, "", "" },
1304 ARG_TERMINATOR 1304 ARG_TERMINATOR
1305 }; 1305 };
1306 /* 1306 /*
1307 * Names of the POIs on miniHomer 1307 * Names of the POIs on miniHomer
1308 */ 1308 */
1309 static const char *poinames[] = { 1309 static const char *poinames[] = {
1310 "Home", "Car", "Boat", "Heart", "Bar" 1310 "Home", "Car", "Boat", "Heart", "Bar"
1311 }; 1311 };
1312 #define NUMPOI (sizeof poinames/sizeof poinames[0]) 1312 #define NUMPOI (sizeof poinames/sizeof poinames[0])
1313 int getPoiByName(char *name) 1313 int getPoiByName(char *name)
1314 { 1314 {
1315 int i; 1315 int i;
1316 for (i=0; i<NUMPOI; i++) { 1316 for (i=0; i<NUMPOI; i++) {
1317 if (strcmp(poinames[i], name) == 0) { 1317 if (strcmp(poinames[i], name) == 0) {
1318 return i; 1318 return i;
1319 } 1319 }
1320 } 1320 }
1321 return -1; 1321 return -1;
1322 } 1322 }
1323 // Convert lla (lat, lng, alt) to ECEF 1323 // Convert lla (lat, lng, alt) to ECEF
1324 // Algorith taken from these sources: 1324 // Algorith taken from these sources:
1325 // http://www.mathworks.com/matlabcentral/fileexchange/7942-covert-lat-lon-alt-to-ecef-cartesian 1325 // http://www.mathworks.com/matlabcentral/fileexchange/7942-covert-lat-lon-alt-to-ecef-cartesian
1326 // http://en.wikipedia.org/wiki/Geodetic_system#From_ECEF_to_geodetic 1326 // http://en.wikipedia.org/wiki/Geodetic_system#From_ECEF_to_geodetic
1327 // http://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf 1327 // http://earth-info.nga.mil/GandG/publications/tr8350.2/wgs84fin.pdf
1328 void lla2ecef(double lat, double lng, double alt, double *ecef_x, double *ecef_y, double *ecef_z) 1328 void lla2ecef(double lat, double lng, double alt, double *ecef_x, double *ecef_y, double *ecef_z)
1329 { 1329 {
1330 long double n; 1330 long double n;
1331 long double a = 6378137.0; 1331 long double a = 6378137.0;
1332 long double esqr = 6.69437999014e-3; 1332 long double esqr = 6.69437999014e-3;
1333 long double s; 1333 long double s;
1334 long double llat, llng, lalt; 1334 long double llat, llng, lalt;
1335 1335
1336 llat=lat*M_PI/180; 1336 llat=lat*M_PI/180;
1337 llng=lng*M_PI/180; 1337 llng=lng*M_PI/180;
1338 lalt=alt; 1338 lalt=alt;
1339 1339
1340 s=sin(llat); 1340 s=sin(llat);
1341 n = a / sqrt(1 - esqr * s*s); 1341 n = a / sqrt(1 - esqr * s*s);
1342 1342
1343 *ecef_x = (double)((n+lalt) * cos(llat) * cos(llng)); 1343 *ecef_x = (double)((n+lalt) * cos(llat) * cos(llng));
1344 *ecef_y = (double)((n+lalt) * cos(llat) * sin(llng)); 1344 *ecef_y = (double)((n+lalt) * cos(llat) * sin(llng));
1345 *ecef_z = (double)((n*(1-esqr) + lalt)* sin(llat)); 1345 *ecef_z = (double)((n*(1-esqr) + lalt)* sin(llat));
1346 } 1346 }
1347 static void miniHomer_get_poi() 1347 static void miniHomer_get_poi()
1348 { 1348 {
1349 gbuint8 MSG_GET_POI[3] = { 0x4D, 0, 0}; 1349 gbuint8 MSG_GET_POI[3] = { 0x4D, 0, 0};
1350 gbuint8 buf[32]; 1350 gbuint8 buf[32];
1351 int poi; 1351 int poi;
1352 double lat, lng, alt; 1352 double lat, lng, alt;
1353 double ecef_x, ecef_y, ecef_z; 1353 double ecef_x, ecef_y, ecef_z;
1354 waypoint *wpt; 1354 waypoint *wpt;
1355 1355
1356 for (poi=0; poi<NUMPOI; poi++) { 1356 for (poi=0; poi<NUMPOI; poi++) {
1357 MSG_GET_POI[1]=(poi>>8)&0xff; 1357 MSG_GET_POI[1]=(poi>>8)&0xff;
1358 MSG_GET_POI[2]=(poi)&0xff; 1358 MSG_GET_POI[2]=(poi)&0xff;
1359 if (skytraq_wr_msg_verify((gbuint8*)&MSG_GET_POI, sizeof(MSG_GET_POI)) != res_OK) { 1359 if (skytraq_wr_msg_verify((gbuint8*)&MSG_GET_POI, sizeof(MSG_GET_POI)) != res_OK) {
1360 warning(MYNAME ": cannot read poi %d '%s'\n", poi, poinames[poi]); 1360 warning(MYNAME ": cannot read poi %d '%s'\n", poi, poinames[poi]);
1361 } 1361 }
1362 skytraq_rd_msg(buf, 25); 1362 skytraq_rd_msg(buf, 25);
1363 ecef_x=be_read_double(buf+1); 1363 ecef_x=be_read_double(buf+1);
1364 ecef_y=be_read_double(buf+9); 1364 ecef_y=be_read_double(buf+9);
1365 ecef_z=be_read_double(buf+17); 1365 ecef_z=be_read_double(buf+17);
1366 1366
1367 // todo - how to determine not-set POIs ? 1367 // todo - how to determine not-set POIs ?
1368 if (ecef_x < 100.0 && ecef_y < 100.0 && ecef_z < 100.0) { 1368 if (ecef_x < 100.0 && ecef_y < 100.0 && ecef_z < 100.0) {
1369 db(2, MYNAME" : skipped poi %d for X=%f, y=%f, Z=%f\n", ecef_x, ecef_y, ecef_z); 1369 db(2, MYNAME" : skipped poi %d for X=%f, y=%f, Z=%f\n", ecef_x, ecef_y, ecef_z);
1370 } else { 1370 } else {
1371 ECEF_to_LLA(ecef_x, ecef_y, ecef_z, &lat, &lng, &alt); 1371 ECEF_to_LLA(ecef_x, ecef_y, ecef_z, &lat, &lng, &alt);
1372 1372
1373 wpt = waypt_new(); 1373 wpt = waypt_new();
1374 xasprintf(&wpt->shortname, "POI_%s", poinames[poi]); 1374 xasprintf(&wpt->shortname, "POI_%s", poinames[poi]);
1375 xasprintf(&wpt->description, "miniHomer points to this coordinates if the %s symbol is on", poinames[poi]); 1375 xasprintf(&wpt->description, "miniHomer points to this coordinates if the %s symbol is on", poinames[poi]);
1376 wpt->latitude = lat; 1376 wpt->latitude = lat;
1377 wpt->longitude = lng; 1377 wpt->longitude = lng;
1378 wpt->altitude = alt; 1378 wpt->altitude = alt;
1379 waypt_add(wpt); 1379 waypt_add(wpt);
1380 db(1, MYNAME ": got POI[%s]='%f %f %f/%f %f %f'\n", poinames[poi], lat, lng, alt, ecef_x, ecef_y, ecef_z); 1380 db(1, MYNAME ": got POI[%s]='%f %f %f/%f %f %f'\n", poinames[poi], lat, lng, alt, ecef_x, ecef_y, ecef_z);
1381 } 1381 }
1382 } 1382 }
1383 } 1383 }
1384 /* 1384 /*
1385 * set lla (lat/lng/alt) specified as <lat>:<lng>[:<alt] for a given poi [0..4] in miniHomer 1385 * set lla (lat/lng/alt) specified as <lat>:<lng>[:<alt] for a given poi [0..4] in miniHomer
1386 * returns 1386 * returns
1387 * 1 if poi was set 1387 * 1 if poi was set
1388 * 0 if opt_poi was not set 1388 * 0 if opt_poi was not set
1389 * -1 in case of errors 1389 * -1 in case of errors
1390 * the number of the POI will not be checked - if it is not correct, miniHome will send NACK 1390 * the number of the POI will not be checked - if it is not correct, miniHome will send NACK
1391 */ 1391 */
1392 static int miniHomer_set_poi(gbuint16 poinum, const char *opt_poi) 1392 static int miniHomer_set_poi(gbuint16 poinum, const char *opt_poi)
1393 { 1393 {
1394 #define MSG_SET_POI_SIZE (sizeof(gbuint8)+sizeof(gbuint16)+3*sizeof(double)+sizeof(gbuint8)) 1394 #define MSG_SET_POI_SIZE (sizeof(gbuint8)+sizeof(gbuint16)+3*sizeof(double)+sizeof(gbuint8))
1395 gbuint8 MSG_SET_POI[MSG_SET_POI_SIZE] = { 1395 gbuint8 MSG_SET_POI[MSG_SET_POI_SIZE] = {
1396 0x4C, 0, 0, // cmd + poi (u16) 1396 0x4C, 0, 0, // cmd + poi (u16)
1397 0, 0, 0, 0, 0, 0, 0, 0, //lat (double ecef) 1397 0, 0, 0, 0, 0, 0, 0, 0, //lat (double ecef)
1398 0, 0, 0, 0, 0, 0, 0, 0, //lng (double ecef) 1398 0, 0, 0, 0, 0, 0, 0, 0, //lng (double ecef)
1399 0, 0, 0, 0, 0, 0, 0, 0, //alt (double ecef) 1399 0, 0, 0, 0, 0, 0, 0, 0, //alt (double ecef)
1400 0 // attr (u8, 1-> to flash, 0->ro sram) 1400 0 // attr (u8, 1-> to flash, 0->ro sram)
1401 }; 1401 };
1402 int n, result; 1402 int n, result;
1403 double lat, lng, alt; 1403 double lat, lng, alt;
1404 double ecef_x, ecef_y, ecef_z; 1404 double ecef_x, ecef_y, ecef_z;
1405 1405
1406 1406
1407 result=0; // result will be 0 if opt_poi isn't set 1407 result=0; // result will be 0 if opt_poi isn't set
1408 if (opt_poi) { // first check opt_poi 1408 if (opt_poi) { // first check opt_poi
1409 if (*opt_poi) { 1409 if (*opt_poi) {
1410 lat=lng=alt=0.0; 1410 lat=lng=alt=0.0;
1411 /* 1411 /*
1412 * parse format of <lat>:<lng>[:alt] 1412 * parse format of <lat>:<lng>[:alt]
1413 * we assume at least two elements in the value string 1413 * we assume at least two elements in the value string
1414 */ 1414 */
1415 n = sscanf(opt_poi, "%lf:%lf:%lf", &lat, &lng, &alt); 1415 n = sscanf(opt_poi, "%lf:%lf:%lf", &lat, &lng, &alt);
1416 if (n >= 2) { 1416 if (n >= 2) {
1417 db(3, "found %d elems '%s':poi=%s@%d, lat=%f, lng=%f, alt=%f over=%s\n", n, opt_poi, poinames[poinum], poinum, lat, lng, alt); 1417 db(3, "found %d elems '%s':poi=%s@%d, lat=%f, lng=%f, alt=%f over=%s\n", n, opt_poi, poinames[poinum], poinum, lat, lng, alt);
1418 lla2ecef(lat, lng, alt, &ecef_x, &ecef_y, &ecef_z); 1418 lla2ecef(lat, lng, alt, &ecef_x, &ecef_y, &ecef_z);
1419 db(1, MYNAME ": set POI[%s]='%f %f %f/%f %f %f'\n", poinames[poinum], lat, lng, alt, ecef_x, ecef_y, ecef_z); 1419 db(1, MYNAME ": set POI[%s]='%f %f %f/%f %f %f'\n", poinames[poinum], lat, lng, alt, ecef_x, ecef_y, ecef_z);
1420 be_write16(MSG_SET_POI+1, poinum); 1420 be_write16(MSG_SET_POI+1, poinum);
1421 be_write_double(MSG_SET_POI+3, ecef_x); 1421 be_write_double(MSG_SET_POI+3, ecef_x);
1422 be_write_double(MSG_SET_POI+11, ecef_y); 1422 be_write_double(MSG_SET_POI+11, ecef_y);
1423 be_write_double(MSG_SET_POI+19, ecef_z); 1423 be_write_double(MSG_SET_POI+19, ecef_z);
1424 MSG_SET_POI[27]=0; 1424 MSG_SET_POI[27]=0;
1425 if (skytraq_wr_msg_verify((gbuint8*)&MSG_SET_POI, sizeof(MSG_SET_POI)) == res_OK) { 1425 if (skytraq_wr_msg_verify((gbuint8*)&MSG_SET_POI, sizeof(MSG_SET_POI)) == res_OK) {
1426 result=1; 1426 result=1;
1427 } else { 1427 } else {
1428 warning(MYNAME ": cannot set poi %d '%s'\n", poinum, poinames[poinum]); 1428 warning(MYNAME ": cannot set poi %d '%s'\n", poinum, poinames[poinum]);
1429 result=-1; 1429 result=-1;
1430 } 1430 }
1431 } else { 1431 } else {
1432 warning(MYNAME ": argument to %s needs to be like <lat>:<lng>[:<alt>]\n", poinames[poinum]); 1432 warning(MYNAME ": argument to %s needs to be like <lat>:<lng>[:<alt>]\n", poinames[poinum]);
1433 result=-1; 1433 result=-1;
1434 } 1434 }
1435 } 1435 }
1436 } 1436 }
1437 return result; 1437 return result;
1438 } 1438 }
1439 static const char *mhport; 1439 static const char *mhport;
1440 static void 1440 static void
1441 miniHomer_rd_init(const char *fname) 1441 miniHomer_rd_init(const char *fname)
1442 { 1442 {
1443 opt_set_location=""; // otherwise it will lead to bus error 1443 opt_set_location=""; // otherwise it will lead to bus error
1444 skytraq_rd_init(fname); // sets global var serial_handle 1444 skytraq_rd_init(fname); // sets global var serial_handle
1445 mhport=fname; 1445 mhport=fname;
1446 } 1446 }
1447 static void 1447 static void
1448 miniHomer_rd_deinit(void) 1448 miniHomer_rd_deinit(void)
1449 { 1449 {
1450 skytraq_rd_deinit(); 1450 skytraq_rd_deinit();
1451 } 1451 }
1452 #define SETPOI(poinum, poiname) if (opt_set_poi_##poiname ) {miniHomer_set_poi(poinum, opt_set_poi_##poiname);} 1452 #define SETPOI(poinum, poiname) if (opt_set_poi_##poiname ) {miniHomer_set_poi(poinum, opt_set_poi_##poiname);}
1453 static void 1453 static void
1454 miniHomer_read(void) 1454 miniHomer_read(void)
1455 { 1455 {
1456 int npoi=0; 1456 int npoi=0;
1457 /* 1457 /*
1458 * read tracks and POI from miniHomer 1458 * read tracks and POI from miniHomer
1459 */ 1459 */
1460 if (miniHomer_set_poi(0, opt_set_poi_home) > 0) { 1460 if (miniHomer_set_poi(0, opt_set_poi_home) > 0) {
1461 npoi++; 1461 npoi++;
1462 } 1462 }
1463 if (miniHomer_set_poi(1, opt_set_poi_car) > 0) { 1463 if (miniHomer_set_poi(1, opt_set_poi_car) > 0) {
1464 npoi++; 1464 npoi++;
1465 } 1465 }
1466 if (miniHomer_set_poi(2, opt_set_poi_boat) > 0) { 1466 if (miniHomer_set_poi(2, opt_set_poi_boat) > 0) {
1467 npoi++; 1467 npoi++;
1468 } 1468 }
1469 if (miniHomer_set_poi(3, opt_set_poi_heart) > 0) { 1469 if (miniHomer_set_poi(3, opt_set_poi_heart) > 0) {
1470 npoi++; 1470 npoi++;
1471 } 1471 }
1472 if (miniHomer_set_poi(4, opt_set_poi_bar) > 0) { 1472 if (miniHomer_set_poi(4, opt_set_poi_bar) > 0) {
1473 npoi++; 1473 npoi++;
1474 } 1474 }
1475 if (npoi == 0) { // do not read if POIs are set (consider set & read distinct operations) 1475 if (npoi == 0) { // do not read if POIs are set (consider set & read distinct operations)
1476 skytraq_read(); // first read tracks (if not supressed by cmd line params) 1476 skytraq_read(); // first read tracks (if not supressed by cmd line params)
1477 // we need this call it initialized waypoint list etc... 1477 // we need this call it initialized waypoint list etc...
1478 skytraq_rd_deinit(); // skytraq_read called system_reset, which changes the baud rate. 1478 skytraq_rd_deinit(); // skytraq_read called system_reset, which changes the baud rate.
1479 1479
1480 skytraq_rd_init(mhport); // Lets start from scratch and re-init the port 1480 skytraq_rd_init(mhport); // Lets start from scratch and re-init the port
1481 miniHomer_get_poi(); // add POI as waypoints to the waypoints of the track 1481 miniHomer_get_poi(); // add POI as waypoints to the waypoints of the track
1482 } 1482 }
1483 } 1483 }
1484 1484
1485 ff_vecs_t miniHomer_vecs = { 1485 ff_vecs_t miniHomer_vecs = {
1486 ff_type_serial, 1486 ff_type_serial,
1487 { 1487 {
1488 ff_cap_read /* waypoints */, 1488 ff_cap_read /* waypoints */,
1489 ff_cap_read /* tracks */, 1489 ff_cap_read /* tracks */,
1490 ff_cap_none /* routes */ 1490 ff_cap_none /* routes */
1491 }, 1491 },
1492 miniHomer_rd_init, 1492 miniHomer_rd_init,
1493 NULL, 1493 NULL,
1494 miniHomer_rd_deinit, 1494 miniHomer_rd_deinit,
1495 NULL, 1495 NULL,
1496 miniHomer_read, 1496 miniHomer_read,
1497 NULL, 1497 NULL,
1498 NULL, 1498 NULL,
1499 miniHomer_args, 1499 miniHomer_args,
1500 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */ 1500 CET_CHARSET_UTF8, 1 /* master process: don't convert anything */
1501 1501
1502 }; 1502 };
Powered by Google Project Hosting