1 | /** |
---|
2 | * Navit, a modular navigation system. |
---|
3 | * Copyright (C) 2005-2008 Navit Team |
---|
4 | * |
---|
5 | * This program is free software; you can redistribute it and/or |
---|
6 | * modify it under the terms of the GNU General Public License |
---|
7 | * version 2 as published by the Free Software Foundation. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, |
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | * GNU General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program; if not, write to the |
---|
16 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
---|
17 | * Boston, MA 02110-1301, USA. |
---|
18 | */ |
---|
19 | |
---|
20 | #include <stdio.h> |
---|
21 | #include <stdlib.h> |
---|
22 | #ifdef HAVE_UNISTD_H |
---|
23 | #include <unistd.h> |
---|
24 | #endif |
---|
25 | #include <fcntl.h> |
---|
26 | #include <string.h> |
---|
27 | #include <glib.h> |
---|
28 | #include <sys/types.h> |
---|
29 | #include <sys/stat.h> |
---|
30 | #include <math.h> |
---|
31 | #include "config.h" |
---|
32 | #include "debug.h" |
---|
33 | #include "callback.h" |
---|
34 | #include "plugin.h" |
---|
35 | #include "coord.h" |
---|
36 | #include "item.h" |
---|
37 | #include "event.h" |
---|
38 | #include "vehicle.h" |
---|
39 | #include <windows.h> |
---|
40 | #include <windowsx.h> |
---|
41 | #include <io.h> |
---|
42 | #include <winioctl.h> |
---|
43 | #include <winbase.h> |
---|
44 | #include <wchar.h> |
---|
45 | #include "support/win32/ConvertUTF.h" |
---|
46 | |
---|
47 | #define SwitchToThread() Sleep(0) |
---|
48 | |
---|
49 | typedef int (WINAPI *PFN_BthSetMode)(DWORD pBthMode); |
---|
50 | typedef int (WINAPI *PFN_BthGetMode)(DWORD* pBthMode); |
---|
51 | |
---|
52 | char *_convert = NULL; |
---|
53 | wchar_t *_wconvert = NULL; |
---|
54 | #define W2A(lpw) (\ |
---|
55 | ((LPCSTR)lpw == NULL) ? NULL : (\ |
---|
56 | _convert = alloca(wcslen(lpw)+1), wcstombs(_convert, lpw, wcslen(lpw) + 1), _convert) ) |
---|
57 | |
---|
58 | #define A2W(lpa) (\ |
---|
59 | ((LPCSTR)lpa == NULL) ? NULL : (\ |
---|
60 | _wconvert = alloca(strlen(lpa)*2+1), mbstowcs(_wconvert, lpa, strlen(lpa) * 2 + 1), _wconvert) ) |
---|
61 | |
---|
62 | static void vehicle_wince_disable_watch(struct vehicle_priv *priv); |
---|
63 | static void vehicle_wince_enable_watch(struct vehicle_priv *priv); |
---|
64 | static int vehicle_wince_parse(struct vehicle_priv *priv, char *buffer); |
---|
65 | static int vehicle_wince_open(struct vehicle_priv *priv); |
---|
66 | static void vehicle_wince_close(struct vehicle_priv *priv); |
---|
67 | |
---|
68 | enum file_type { |
---|
69 | file_type_pipe = 1, file_type_device, file_type_file, file_type_socket |
---|
70 | }; |
---|
71 | |
---|
72 | static int buffer_size = 1024; |
---|
73 | |
---|
74 | struct gps_sat { |
---|
75 | int prn; |
---|
76 | int elevation; |
---|
77 | int azimuth; |
---|
78 | int snr; |
---|
79 | }; |
---|
80 | |
---|
81 | |
---|
82 | struct vehicle_priv { |
---|
83 | char *source; |
---|
84 | struct callback_list *cbl; |
---|
85 | struct callback_list *priv_cbl; |
---|
86 | int is_running; |
---|
87 | int thread_up; |
---|
88 | int fd; |
---|
89 | HANDLE m_hGPSDevice; // Handle to the device |
---|
90 | HANDLE m_hGPSThread; // Handle to the thread |
---|
91 | DWORD m_dwGPSThread; // Thread id |
---|
92 | |
---|
93 | char *buffer; |
---|
94 | int buffer_pos; |
---|
95 | char *read_buffer; |
---|
96 | int read_buffer_pos; |
---|
97 | char *nmea_data; |
---|
98 | char *nmea_data_buf; |
---|
99 | |
---|
100 | struct coord_geo geo; |
---|
101 | double speed; |
---|
102 | double direction; |
---|
103 | double height; |
---|
104 | double hdop; |
---|
105 | double vdop; |
---|
106 | char fixtime[20]; |
---|
107 | int fixyear; |
---|
108 | int fixmonth; |
---|
109 | int fixday; |
---|
110 | int status; |
---|
111 | int sats_used; |
---|
112 | int sats_visible; |
---|
113 | int sats_signal; |
---|
114 | int time; |
---|
115 | int on_eof; |
---|
116 | int baudrate; |
---|
117 | enum file_type file_type; |
---|
118 | struct attr ** attrs; |
---|
119 | char fixiso8601[128]; |
---|
120 | int checksum_ignore; |
---|
121 | HMODULE hBthDll; |
---|
122 | PFN_BthSetMode BthSetMode; |
---|
123 | int magnetic_direction; |
---|
124 | int current_count; |
---|
125 | struct gps_sat current[24]; |
---|
126 | int next_count; |
---|
127 | struct gps_sat next[24]; |
---|
128 | struct item sat_item; |
---|
129 | int valid; |
---|
130 | int has_data; |
---|
131 | GMutex lock; |
---|
132 | }; |
---|
133 | |
---|
134 | static void initBth(struct vehicle_priv *priv) |
---|
135 | { |
---|
136 | |
---|
137 | BOOL succeeded = FALSE; |
---|
138 | priv->hBthDll = LoadLibrary(TEXT("bthutil.dll")); |
---|
139 | if ( priv->hBthDll ) |
---|
140 | { |
---|
141 | DWORD bthMode; |
---|
142 | PFN_BthGetMode BthGetMode = (PFN_BthGetMode)GetProcAddress(priv->hBthDll, TEXT("BthGetMode") ); |
---|
143 | |
---|
144 | if ( BthGetMode && BthGetMode(&bthMode) == ERROR_SUCCESS && bthMode == 0 ) |
---|
145 | { |
---|
146 | priv->BthSetMode = (PFN_BthSetMode)GetProcAddress(priv->hBthDll, TEXT("BthSetMode") ); |
---|
147 | if( priv->BthSetMode && priv->BthSetMode(1) == ERROR_SUCCESS ) |
---|
148 | { |
---|
149 | dbg(1, "bluetooth activated\n"); |
---|
150 | succeeded = TRUE; |
---|
151 | } |
---|
152 | } |
---|
153 | |
---|
154 | } |
---|
155 | else |
---|
156 | { |
---|
157 | dbg(0, "Bluetooth library notfound\n"); |
---|
158 | } |
---|
159 | |
---|
160 | if ( !succeeded ) |
---|
161 | { |
---|
162 | |
---|
163 | dbg(1, "Bluetooth already enabled or failed to enable it.\n"); |
---|
164 | priv->BthSetMode = NULL; |
---|
165 | if ( priv->hBthDll ) |
---|
166 | { |
---|
167 | FreeLibrary(priv->hBthDll); |
---|
168 | } |
---|
169 | } |
---|
170 | } |
---|
171 | |
---|
172 | static int initDevice(struct vehicle_priv *priv) |
---|
173 | { |
---|
174 | COMMTIMEOUTS commTiming; |
---|
175 | HANDLE hGPS; |
---|
176 | if ( priv->m_hGPSDevice ) |
---|
177 | CloseHandle(priv->m_hGPSDevice); |
---|
178 | |
---|
179 | if ( priv->file_type == file_type_device ) |
---|
180 | { |
---|
181 | dbg(0, "Init Device\n"); |
---|
182 | /* GPD0 is the control port for the GPS driver */ |
---|
183 | hGPS = CreateFile(L"GPD0:", GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); |
---|
184 | if (hGPS != INVALID_HANDLE_VALUE) |
---|
185 | { |
---|
186 | #ifndef IOCTL_SERVICE_REFRESH |
---|
187 | #define IOCTL_SERVICE_REFRESH 0x4100000C |
---|
188 | #endif |
---|
189 | DeviceIoControl(hGPS,IOCTL_SERVICE_REFRESH,0,0,0,0,0,0); |
---|
190 | #ifndef IOCTL_SERVICE_START |
---|
191 | #define IOCTL_SERVICE_START 0x41000004 |
---|
192 | #endif |
---|
193 | DeviceIoControl(hGPS,IOCTL_SERVICE_START,0,0,0,0,0,0); |
---|
194 | CloseHandle(hGPS); |
---|
195 | } |
---|
196 | |
---|
197 | while (priv->is_running && |
---|
198 | (priv->m_hGPSDevice = CreateFile(A2W(priv->source), |
---|
199 | GENERIC_READ, 0, |
---|
200 | NULL, OPEN_EXISTING, 0, 0)) == INVALID_HANDLE_VALUE) |
---|
201 | { |
---|
202 | Sleep(1000); |
---|
203 | dbg(0, "Waiting to connect to %s\n", priv->source); |
---|
204 | } |
---|
205 | GetCommTimeouts (priv->m_hGPSDevice, &commTiming); |
---|
206 | commTiming.ReadIntervalTimeout = 20; |
---|
207 | commTiming.ReadTotalTimeoutMultiplier = 0; |
---|
208 | commTiming.ReadTotalTimeoutConstant = 200; |
---|
209 | |
---|
210 | commTiming.WriteTotalTimeoutMultiplier=5; |
---|
211 | commTiming.WriteTotalTimeoutConstant=5; |
---|
212 | SetCommTimeouts (priv->m_hGPSDevice, &commTiming); |
---|
213 | |
---|
214 | if (priv->baudrate) |
---|
215 | { |
---|
216 | DCB portState; |
---|
217 | if (!GetCommState(priv->m_hGPSDevice, &portState)) |
---|
218 | { |
---|
219 | MessageBox (NULL, TEXT ("GetCommState Error"), TEXT (""), |
---|
220 | MB_APPLMODAL|MB_OK); |
---|
221 | priv->thread_up = 0; |
---|
222 | return 0; |
---|
223 | } |
---|
224 | portState.BaudRate = priv->baudrate; |
---|
225 | if (!SetCommState(priv->m_hGPSDevice, &portState)) |
---|
226 | { |
---|
227 | MessageBox (NULL, TEXT ("SetCommState Error"), TEXT (""), |
---|
228 | MB_APPLMODAL|MB_OK); |
---|
229 | priv->thread_up = 0; |
---|
230 | return 0; |
---|
231 | } |
---|
232 | } |
---|
233 | |
---|
234 | } |
---|
235 | else |
---|
236 | { |
---|
237 | dbg(0, "Open File\n"); |
---|
238 | priv->m_hGPSDevice = CreateFileW( A2W(priv->source), |
---|
239 | GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, 0); |
---|
240 | if ( priv->m_hGPSDevice == INVALID_HANDLE_VALUE) |
---|
241 | { |
---|
242 | dbg(0, "Could not open %s\n", priv->source); |
---|
243 | return 0; |
---|
244 | } |
---|
245 | } |
---|
246 | return 1; |
---|
247 | |
---|
248 | } |
---|
249 | |
---|
250 | static int read_win32(struct vehicle_priv *priv, char *buffer, size_t size) |
---|
251 | { |
---|
252 | int ret_size; |
---|
253 | |
---|
254 | g_mutex_lock(&priv->lock); |
---|
255 | ret_size = MIN(size,priv->read_buffer_pos); |
---|
256 | priv->has_data = 0; |
---|
257 | memcpy(buffer, priv->read_buffer, ret_size); |
---|
258 | |
---|
259 | memmove(priv->read_buffer, priv->read_buffer + ret_size, buffer_size - ret_size); |
---|
260 | priv->read_buffer_pos -= ret_size; |
---|
261 | g_mutex_unlock(&priv->lock); |
---|
262 | return ret_size; |
---|
263 | } |
---|
264 | |
---|
265 | static DWORD WINAPI wince_reader_thread (LPVOID lParam) |
---|
266 | { |
---|
267 | struct vehicle_priv *priv = lParam; |
---|
268 | char chunk_buffer[3*82]; |
---|
269 | BOOL status; |
---|
270 | DWORD bytes_read; |
---|
271 | int waitcounter; |
---|
272 | int nullcounter; |
---|
273 | |
---|
274 | |
---|
275 | dbg(0, "GPS Port:[%s]\n", priv->source); |
---|
276 | priv->thread_up = 1; |
---|
277 | |
---|
278 | if ( !initDevice(priv) ) { |
---|
279 | return -1; |
---|
280 | } |
---|
281 | nullcounter = 0; |
---|
282 | |
---|
283 | while (priv->is_running) |
---|
284 | { |
---|
285 | // dbg(1,"readfile"); |
---|
286 | waitcounter = 0; |
---|
287 | status = ReadFile(priv->m_hGPSDevice, |
---|
288 | chunk_buffer, sizeof(chunk_buffer), |
---|
289 | &bytes_read, NULL); |
---|
290 | dbg(1,"readfile %d\n", bytes_read); |
---|
291 | |
---|
292 | if ( !status ) |
---|
293 | { |
---|
294 | dbg(0,"Error reading file/device. Try again.\n"); |
---|
295 | initDevice(priv); |
---|
296 | continue; |
---|
297 | } else |
---|
298 | { |
---|
299 | if ( !bytes_read ) |
---|
300 | { |
---|
301 | nullcounter++; |
---|
302 | if (nullcounter >= 3) |
---|
303 | { |
---|
304 | dbg(0,"Nmea port muted.\n"); |
---|
305 | initDevice(priv); |
---|
306 | nullcounter = 0; |
---|
307 | continue; |
---|
308 | |
---|
309 | } |
---|
310 | } else |
---|
311 | { |
---|
312 | nullcounter = 0; |
---|
313 | } |
---|
314 | |
---|
315 | } |
---|
316 | |
---|
317 | while ( priv->read_buffer_pos + bytes_read > buffer_size ) |
---|
318 | { |
---|
319 | |
---|
320 | /* TODO (rikky#1#): should use blocking */ |
---|
321 | if ( priv->file_type != file_type_file ) |
---|
322 | { |
---|
323 | dbg(0, "GPS data comes too fast. Have to wait here %d\n", priv->read_buffer_pos + bytes_read - buffer_size); |
---|
324 | } |
---|
325 | |
---|
326 | Sleep(50); |
---|
327 | waitcounter++; |
---|
328 | if ( waitcounter % 8 == 0 ) |
---|
329 | { |
---|
330 | dbg(0, "Remind them of the data\n"); |
---|
331 | event_call_callback(priv->priv_cbl); |
---|
332 | } |
---|
333 | |
---|
334 | } |
---|
335 | |
---|
336 | g_mutex_lock(&priv->lock); |
---|
337 | memcpy(priv->read_buffer + priv->read_buffer_pos , chunk_buffer, bytes_read ); |
---|
338 | |
---|
339 | priv->read_buffer_pos += bytes_read; |
---|
340 | |
---|
341 | if ( !priv->has_data ) |
---|
342 | { |
---|
343 | event_call_callback(priv->priv_cbl); |
---|
344 | priv->has_data = 1; |
---|
345 | } |
---|
346 | |
---|
347 | g_mutex_unlock(&priv->lock); |
---|
348 | |
---|
349 | } |
---|
350 | return TRUE; |
---|
351 | } |
---|
352 | |
---|
353 | static int |
---|
354 | vehicle_wince_available_ports(void) |
---|
355 | { |
---|
356 | HKEY hkResult; |
---|
357 | HKEY hkSubResult; |
---|
358 | wchar_t keyname[20]; |
---|
359 | wchar_t devicename[100]; |
---|
360 | wchar_t devicetype[100]; |
---|
361 | int index = 0; |
---|
362 | DWORD regkey_length = sizeof(keyname); |
---|
363 | DWORD regdevtype_length = sizeof(devicetype); |
---|
364 | |
---|
365 | RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("Drivers\\Active"), 0, 0, &hkResult); |
---|
366 | while (RegEnumKeyEx( hkResult, index++, keyname, ®key_length, NULL, NULL, NULL, NULL) == ERROR_SUCCESS ) |
---|
367 | { |
---|
368 | if (RegOpenKeyEx( hkResult, keyname, 0, 0, &hkSubResult) == ERROR_SUCCESS ) |
---|
369 | { |
---|
370 | regkey_length = sizeof(keyname); |
---|
371 | if ( RegQueryValueEx( hkSubResult, L"Name", NULL, NULL, (LPBYTE)devicename, ®key_length) == ERROR_SUCCESS ) |
---|
372 | { |
---|
373 | regdevtype_length = sizeof(devicetype); |
---|
374 | if ( RegQueryValueEx( hkSubResult, L"Key", NULL, NULL, (LPBYTE)devicetype, ®devtype_length) == ERROR_SUCCESS ) |
---|
375 | { |
---|
376 | dbg(0, "Found device '%s' (%s)\n", W2A(devicename), W2A(devicetype)); |
---|
377 | } |
---|
378 | else |
---|
379 | { |
---|
380 | dbg(0, "Found device '%s'\n", W2A(devicename)); |
---|
381 | } |
---|
382 | } |
---|
383 | RegCloseKey(hkSubResult); |
---|
384 | } |
---|
385 | regkey_length = sizeof(keyname); |
---|
386 | } |
---|
387 | |
---|
388 | RegCloseKey(hkResult); |
---|
389 | return 0; |
---|
390 | } |
---|
391 | |
---|
392 | |
---|
393 | static int |
---|
394 | vehicle_wince_open(struct vehicle_priv *priv) |
---|
395 | { |
---|
396 | char* raw_setting_str; |
---|
397 | char* strport; |
---|
398 | char* strsettings; |
---|
399 | |
---|
400 | dbg(1, "enter vehicle_wince_open, priv->source='%s'\n", priv->source); |
---|
401 | |
---|
402 | if (priv->source ) { |
---|
403 | |
---|
404 | if ( strcmp(priv->source, "list") == 0 ) |
---|
405 | { |
---|
406 | vehicle_wince_available_ports(); |
---|
407 | return 0; |
---|
408 | } |
---|
409 | |
---|
410 | raw_setting_str = g_strdup( priv->source ); |
---|
411 | strport = strchr(raw_setting_str, ':' ); |
---|
412 | strsettings = strchr(raw_setting_str, ' ' ); |
---|
413 | |
---|
414 | if (raw_setting_str && strport&&strsettings ) { |
---|
415 | strport++; |
---|
416 | *strsettings = '\0'; |
---|
417 | strsettings++; |
---|
418 | |
---|
419 | dbg(0, "serial('%s', '%s')\n", strport, strsettings ); |
---|
420 | } |
---|
421 | if (raw_setting_str) |
---|
422 | g_free( raw_setting_str ); |
---|
423 | } |
---|
424 | return 1; |
---|
425 | } |
---|
426 | |
---|
427 | static void |
---|
428 | vehicle_wince_close(struct vehicle_priv *priv) |
---|
429 | { |
---|
430 | dbg(1,"enter"); |
---|
431 | } |
---|
432 | |
---|
433 | static int |
---|
434 | vehicle_wince_parse(struct vehicle_priv *priv, char *buffer) |
---|
435 | { |
---|
436 | char *nmea_data_buf, *p, *item[32]; |
---|
437 | double lat, lng; |
---|
438 | int i, j, bcsum; |
---|
439 | int len = strlen(buffer); |
---|
440 | unsigned char csum = 0; |
---|
441 | int valid=0; |
---|
442 | int ret = 0; |
---|
443 | |
---|
444 | dbg(2, "enter: buffer='%s'\n", buffer); |
---|
445 | for (;;) { |
---|
446 | if (len < 4) { |
---|
447 | dbg(0, "'%s' too short\n", buffer); |
---|
448 | return ret; |
---|
449 | } |
---|
450 | if (buffer[len - 1] == '\r' || buffer[len - 1] == '\n') { |
---|
451 | buffer[--len] = '\0'; |
---|
452 | if (buffer[len - 1] == '\r') |
---|
453 | buffer[--len] = '\0'; |
---|
454 | } else |
---|
455 | break; |
---|
456 | } |
---|
457 | if (buffer[0] != '$') { |
---|
458 | dbg(0, "no leading $ in '%s'\n", buffer); |
---|
459 | return ret; |
---|
460 | } |
---|
461 | if (buffer[len - 3] != '*') { |
---|
462 | dbg(0, "no *XX in '%s'\n", buffer); |
---|
463 | return ret; |
---|
464 | } |
---|
465 | for (i = 1; i < len - 3; i++) { |
---|
466 | csum ^= (unsigned char) (buffer[i]); |
---|
467 | } |
---|
468 | if (!sscanf(buffer + len - 2, "%x", &bcsum) && priv->checksum_ignore != 2) { |
---|
469 | dbg(0, "no checksum in '%s'\n", buffer); |
---|
470 | return ret; |
---|
471 | } |
---|
472 | if (bcsum != csum && priv->checksum_ignore == 0) { |
---|
473 | dbg(0, "wrong checksum in '%s'\n", buffer); |
---|
474 | return ret; |
---|
475 | } |
---|
476 | |
---|
477 | if (!priv->nmea_data_buf || strlen(priv->nmea_data_buf) < 65536) { |
---|
478 | nmea_data_buf=g_strconcat(priv->nmea_data_buf ? priv->nmea_data_buf : "", buffer, "\n", NULL); |
---|
479 | g_free(priv->nmea_data_buf); |
---|
480 | priv->nmea_data_buf=nmea_data_buf; |
---|
481 | } else { |
---|
482 | dbg(0, "nmea buffer overflow, discarding '%s'\n", buffer); |
---|
483 | } |
---|
484 | i = 0; |
---|
485 | p = buffer; |
---|
486 | while (i < 31) { |
---|
487 | item[i++] = p; |
---|
488 | while (*p && *p != ',') |
---|
489 | p++; |
---|
490 | if (!*p) |
---|
491 | break; |
---|
492 | *p++ = '\0'; |
---|
493 | } |
---|
494 | |
---|
495 | if (!strncmp(buffer, "$GPGGA", 6)) { |
---|
496 | /* 1 1111 |
---|
497 | 0 1 2 3 4 5 6 7 8 9 0 1234 |
---|
498 | $GPGGA,184424.505,4924.2811,N,01107.8846,E,1,05,2.5,408.6,M,,,,0000*0C |
---|
499 | UTC of Fix[1],Latitude[2],N/S[3],Longitude[4],E/W[5],Quality(0=inv,1=gps,2=dgps)[6],Satelites used[7], |
---|
500 | HDOP[8],Altitude[9],"M"[10],height of geoid[11], "M"[12], time since dgps update[13], dgps ref station [14] |
---|
501 | */ |
---|
502 | if (*item[2] && *item[3] && *item[4] && *item[5]) { |
---|
503 | lat = g_ascii_strtod(item[2], NULL); |
---|
504 | priv->geo.lat = floor(lat / 100); |
---|
505 | lat -= priv->geo.lat * 100; |
---|
506 | priv->geo.lat += lat / 60; |
---|
507 | |
---|
508 | if (!g_strcasecmp(item[3],"S")) |
---|
509 | priv->geo.lat=-priv->geo.lat; |
---|
510 | |
---|
511 | lng = g_ascii_strtod(item[4], NULL); |
---|
512 | priv->geo.lng = floor(lng / 100); |
---|
513 | lng -= priv->geo.lng * 100; |
---|
514 | priv->geo.lng += lng / 60; |
---|
515 | |
---|
516 | if (!g_strcasecmp(item[5],"W")) |
---|
517 | priv->geo.lng=-priv->geo.lng; |
---|
518 | priv->valid=attr_position_valid_valid; |
---|
519 | dbg(2, "latitude '%2.4f' longitude %2.4f\n", priv->geo.lat, priv->geo.lng); |
---|
520 | |
---|
521 | } else |
---|
522 | priv->valid=attr_position_valid_invalid; |
---|
523 | if (*item[6]) { |
---|
524 | sscanf(item[6], "%d", &priv->status); |
---|
525 | /* check if a valid fix has been sent on NMEA port to allow callback */ |
---|
526 | if (priv->status >= 1) |
---|
527 | ret = 1; |
---|
528 | } |
---|
529 | if (*item[7]) |
---|
530 | sscanf(item[7], "%d", &priv->sats_used); |
---|
531 | if (*item[8]) |
---|
532 | sscanf(item[8], "%lf", &priv->hdop); |
---|
533 | if (*item[1]) |
---|
534 | strncpy(priv->fixtime, item[1], sizeof(priv->fixtime)); |
---|
535 | if (*item[9]) |
---|
536 | sscanf(item[9], "%lf", &priv->height); |
---|
537 | |
---|
538 | g_free(priv->nmea_data); |
---|
539 | priv->nmea_data=priv->nmea_data_buf; |
---|
540 | priv->nmea_data_buf=NULL; |
---|
541 | } |
---|
542 | if (!strncmp(buffer, "$GPVTG", 6)) { |
---|
543 | /* 0 1 2 34 5 6 7 8 |
---|
544 | $GPVTG,143.58,T,,M,0.26,N,0.5,K*6A |
---|
545 | Course Over Ground Degrees True[1],"T"[2],Course Over Ground Degrees Magnetic[3],"M"[4], |
---|
546 | Speed in Knots[5],"N"[6],"Speed in KM/H"[7],"K"[8] |
---|
547 | */ |
---|
548 | if (item[1] && item[7]) |
---|
549 | valid = 1; |
---|
550 | if (i >= 10 && (*item[9] == 'A' || *item[9] == 'D')) |
---|
551 | valid = 1; |
---|
552 | if (valid) { |
---|
553 | priv->direction = g_ascii_strtod( item[1], NULL ); |
---|
554 | priv->speed = g_ascii_strtod( item[7], NULL ); |
---|
555 | dbg(2,"direction %lf, speed %2.1lf\n", priv->direction, priv->speed); |
---|
556 | } |
---|
557 | } |
---|
558 | if (!strncmp(buffer, "$GPRMC", 6)) { |
---|
559 | /* 1 1 |
---|
560 | 0 1 2 3 4 5 6 7 8 9 0 1 |
---|
561 | $GPRMC,123519,A,4807.038,N,01131.000,E,022.4,084.4,230394,003.1,W*6A |
---|
562 | Time[1],Active/Void[2],lat[3],N/S[4],long[5],W/E[6],speed in knots[7],track angle[8],date[9], |
---|
563 | magnetic variation[10],magnetic variation direction[11] |
---|
564 | */ |
---|
565 | if (*item[2] == 'A') |
---|
566 | valid = 1; |
---|
567 | if (i >= 13 && (*item[12] == 'A' || *item[12] == 'D')) |
---|
568 | valid = 1; |
---|
569 | if (valid) { |
---|
570 | priv->direction = g_ascii_strtod( item[8], NULL ); |
---|
571 | priv->speed = g_ascii_strtod( item[7], NULL ); |
---|
572 | priv->speed *= 1.852; |
---|
573 | sscanf(item[9], "%02d%02d%02d", |
---|
574 | &priv->fixday, |
---|
575 | &priv->fixmonth, |
---|
576 | &priv->fixyear); |
---|
577 | priv->fixyear += 2000; |
---|
578 | } |
---|
579 | ret = 1; |
---|
580 | } |
---|
581 | if (!strncmp(buffer, "$GPGSV", 6) && i >= 4) { |
---|
582 | /* |
---|
583 | 0 GSV Satellites in view |
---|
584 | 1 2 Number of sentences for full data |
---|
585 | 2 1 sentence 1 of 2 |
---|
586 | 3 08 Number of satellites in view |
---|
587 | |
---|
588 | 4 01 Satellite PRN number |
---|
589 | 5 40 Elevation, degrees |
---|
590 | 6 083 Azimuth, degrees |
---|
591 | 7 46 SNR - higher is better |
---|
592 | for up to 4 satellites per sentence |
---|
593 | *75 the checksum data, always begins with * |
---|
594 | */ |
---|
595 | if (item[3]) { |
---|
596 | sscanf(item[3], "%d", &priv->sats_visible); |
---|
597 | } |
---|
598 | j=4; |
---|
599 | while (j+4 <= i && priv->current_count < 24) { |
---|
600 | struct gps_sat *sat=&priv->next[priv->next_count++]; |
---|
601 | sat->prn=atoi(item[j]); |
---|
602 | sat->elevation=atoi(item[j+1]); |
---|
603 | sat->azimuth=atoi(item[j+2]); |
---|
604 | sat->snr=atoi(item[j+3]); |
---|
605 | j+=4; |
---|
606 | } |
---|
607 | if (!strcmp(item[1], item[2])) { |
---|
608 | priv->sats_signal=0; |
---|
609 | for (i = 0 ; i < priv->next_count ; i++) { |
---|
610 | priv->current[i]=priv->next[i]; |
---|
611 | if (priv->current[i].snr) |
---|
612 | priv->sats_signal++; |
---|
613 | } |
---|
614 | priv->current_count=priv->next_count; |
---|
615 | priv->next_count=0; |
---|
616 | } |
---|
617 | } |
---|
618 | if (!strncmp(buffer, "$GPZDA", 6)) { |
---|
619 | /* |
---|
620 | 0 1 2 3 4 5 6 |
---|
621 | $GPZDA,hhmmss.ss,dd,mm,yyyy,xx,yy*CC |
---|
622 | hhmmss HrMinSec(UTC) |
---|
623 | dd,mm,yyy Day,Month,Year |
---|
624 | xx local zone hours -13..13 |
---|
625 | yy local zone minutes 0..59 |
---|
626 | */ |
---|
627 | if (item[1] && item[2] && item[3] && item[4]) { |
---|
628 | strncpy(priv->fixtime, item[1], strlen(priv->fixtime)); |
---|
629 | priv->fixday = atoi(item[2]); |
---|
630 | priv->fixmonth = atoi(item[3]); |
---|
631 | priv->fixyear = atoi(item[4]); |
---|
632 | } |
---|
633 | } |
---|
634 | if (!strncmp(buffer, "$IISMD", 6)) { |
---|
635 | /* |
---|
636 | 0 1 2 3 4 |
---|
637 | $IISMD,dir,press,height,temp*CC" |
---|
638 | dir Direction (0-359) |
---|
639 | press Pressure (hpa, i.e. 1032) |
---|
640 | height Barometric height above ground (meter) |
---|
641 | temp Temperature (Degree Celsius) |
---|
642 | */ |
---|
643 | if (item[1]) { |
---|
644 | priv->magnetic_direction = g_ascii_strtod( item[1], NULL ); |
---|
645 | dbg(1,"magnetic %d\n", priv->magnetic_direction); |
---|
646 | } |
---|
647 | } |
---|
648 | return ret; |
---|
649 | } |
---|
650 | |
---|
651 | static void |
---|
652 | vehicle_wince_io(struct vehicle_priv *priv) |
---|
653 | { |
---|
654 | int size, rc = 0; |
---|
655 | char *str, *tok; |
---|
656 | |
---|
657 | dbg(1, "vehicle_file_io : enter\n"); |
---|
658 | |
---|
659 | size = read_win32(priv, priv->buffer + priv->buffer_pos, buffer_size - priv->buffer_pos - 1); |
---|
660 | |
---|
661 | if (size <= 0) { |
---|
662 | switch (priv->on_eof) { |
---|
663 | case 0: |
---|
664 | vehicle_wince_close(priv); |
---|
665 | vehicle_wince_open(priv); |
---|
666 | break; |
---|
667 | case 1: |
---|
668 | vehicle_wince_disable_watch(priv); |
---|
669 | break; |
---|
670 | case 2: |
---|
671 | exit(0); |
---|
672 | break; |
---|
673 | } |
---|
674 | return; |
---|
675 | } |
---|
676 | priv->buffer_pos += size; |
---|
677 | priv->buffer[priv->buffer_pos] = '\0'; |
---|
678 | dbg(1, "size=%d pos=%d buffer='%s'\n", size, |
---|
679 | priv->buffer_pos, priv->buffer); |
---|
680 | str = priv->buffer; |
---|
681 | while ((tok = strchr(str, '\n'))) { |
---|
682 | *tok++ = '\0'; |
---|
683 | dbg(1, "line='%s'\n", str); |
---|
684 | rc +=vehicle_wince_parse(priv, str); |
---|
685 | str = tok; |
---|
686 | if (priv->file_type == file_type_file && rc) |
---|
687 | break; |
---|
688 | } |
---|
689 | |
---|
690 | if (str != priv->buffer) { |
---|
691 | size = priv->buffer + priv->buffer_pos - str; |
---|
692 | memmove(priv->buffer, str, size + 1); |
---|
693 | priv->buffer_pos = size; |
---|
694 | dbg(2, "now pos=%d buffer='%s'\n", |
---|
695 | priv->buffer_pos, priv->buffer); |
---|
696 | } else if (priv->buffer_pos == buffer_size - 1) { |
---|
697 | dbg(0, |
---|
698 | "Overflow. Most likely wrong baud rate or no nmea protocol\n"); |
---|
699 | priv->buffer_pos = 0; |
---|
700 | } |
---|
701 | if (rc) |
---|
702 | callback_list_call_attr_0(priv->cbl, attr_position_coord_geo); |
---|
703 | } |
---|
704 | |
---|
705 | static void |
---|
706 | vehicle_wince_enable_watch(struct vehicle_priv *priv) |
---|
707 | { |
---|
708 | dbg(1, "enter"); |
---|
709 | vehicle_wince_disable_watch(priv); |
---|
710 | priv->is_running = 1; |
---|
711 | |
---|
712 | InitializeCriticalSection(&priv->lock); |
---|
713 | priv->m_hGPSThread = CreateThread(NULL, 0, wince_reader_thread, |
---|
714 | priv, 0, &priv->m_dwGPSThread); |
---|
715 | |
---|
716 | if (!priv->m_hGPSThread) { |
---|
717 | priv->is_running = 0; |
---|
718 | // error creating thread |
---|
719 | MessageBox (NULL, TEXT ("Can not create GPS reader thread"), TEXT (""), |
---|
720 | MB_APPLMODAL|MB_OK); |
---|
721 | } |
---|
722 | } |
---|
723 | |
---|
724 | static void |
---|
725 | vehicle_wince_disable_watch(struct vehicle_priv *priv) |
---|
726 | { |
---|
727 | int wait = 5000; |
---|
728 | |
---|
729 | dbg(1, "enter"); |
---|
730 | |
---|
731 | priv->is_running = 0; |
---|
732 | while (wait-- > 0 && priv->thread_up) { |
---|
733 | SwitchToThread(); |
---|
734 | } |
---|
735 | if (priv->m_hGPSThread) { |
---|
736 | // Terminate reader, sorry |
---|
737 | TerminateThread(priv->m_hGPSThread, -1); |
---|
738 | } |
---|
739 | } |
---|
740 | |
---|
741 | |
---|
742 | static void |
---|
743 | vehicle_wince_destroy(struct vehicle_priv *priv) |
---|
744 | { |
---|
745 | vehicle_wince_disable_watch(priv); |
---|
746 | vehicle_wince_close(priv); |
---|
747 | if (priv->BthSetMode) |
---|
748 | { |
---|
749 | (void)priv->BthSetMode(0); |
---|
750 | FreeLibrary(priv->hBthDll); |
---|
751 | } |
---|
752 | if (priv->source) |
---|
753 | g_free(priv->source); |
---|
754 | if (priv->buffer) |
---|
755 | g_free(priv->buffer); |
---|
756 | if (priv->read_buffer) |
---|
757 | g_free(priv->read_buffer); |
---|
758 | g_free(priv); |
---|
759 | } |
---|
760 | |
---|
761 | static int |
---|
762 | vehicle_wince_position_attr_get(struct vehicle_priv *priv, |
---|
763 | enum attr_type type, struct attr *attr) |
---|
764 | { |
---|
765 | switch (type) { |
---|
766 | case attr_position_fix_type: |
---|
767 | attr->u.num = priv->status; |
---|
768 | break; |
---|
769 | case attr_position_height: |
---|
770 | attr->u.numd = &priv->height; |
---|
771 | break; |
---|
772 | case attr_position_speed: |
---|
773 | attr->u.numd = &priv->speed; |
---|
774 | break; |
---|
775 | case attr_position_direction: |
---|
776 | attr->u.numd = &priv->direction; |
---|
777 | break; |
---|
778 | case attr_position_magnetic_direction: |
---|
779 | attr->u.num = priv->magnetic_direction; |
---|
780 | break; |
---|
781 | case attr_position_hdop: |
---|
782 | attr->u.numd = &priv->hdop; |
---|
783 | break; |
---|
784 | case attr_position_qual: |
---|
785 | attr->u.num = priv->sats_visible; |
---|
786 | break; |
---|
787 | case attr_position_sats_signal: |
---|
788 | attr->u.num = priv->sats_signal; |
---|
789 | break; |
---|
790 | case attr_position_sats_used: |
---|
791 | attr->u.num = priv->sats_used; |
---|
792 | break; |
---|
793 | case attr_position_coord_geo: |
---|
794 | attr->u.coord_geo = &priv->geo; |
---|
795 | break; |
---|
796 | case attr_position_nmea: |
---|
797 | attr->u.str=priv->nmea_data; |
---|
798 | if (! attr->u.str) |
---|
799 | return 0; |
---|
800 | break; |
---|
801 | case attr_position_time_iso8601: |
---|
802 | if (!priv->fixyear || !priv->fixtime[0]) |
---|
803 | return 0; |
---|
804 | sprintf(priv->fixiso8601, "%04d-%02d-%02dT%.2s:%.2s:%sZ", |
---|
805 | priv->fixyear, priv->fixmonth, priv->fixday, |
---|
806 | priv->fixtime, (priv->fixtime+2), (priv->fixtime+4)); |
---|
807 | attr->u.str=priv->fixiso8601; |
---|
808 | break; |
---|
809 | case attr_position_sat_item: |
---|
810 | dbg(0,"at here\n"); |
---|
811 | priv->sat_item.id_lo++; |
---|
812 | if (priv->sat_item.id_lo > priv->current_count) { |
---|
813 | priv->sat_item.id_lo=0; |
---|
814 | return 0; |
---|
815 | } |
---|
816 | attr->u.item=&priv->sat_item; |
---|
817 | break; |
---|
818 | case attr_position_valid: |
---|
819 | attr->u.num=priv->valid; |
---|
820 | break; |
---|
821 | default: |
---|
822 | return 0; |
---|
823 | } |
---|
824 | if (type != attr_position_sat_item) |
---|
825 | priv->sat_item.id_lo=0; |
---|
826 | attr->type = type; |
---|
827 | return 1; |
---|
828 | } |
---|
829 | |
---|
830 | static int |
---|
831 | vehicle_wince_sat_attr_get(void *priv_data, enum attr_type type, struct attr *attr) |
---|
832 | { |
---|
833 | struct vehicle_priv *priv=priv_data; |
---|
834 | struct gps_sat *sat; |
---|
835 | |
---|
836 | if (priv->sat_item.id_lo < 1) |
---|
837 | return 0; |
---|
838 | if (priv->sat_item.id_lo > priv->current_count) |
---|
839 | return 0; |
---|
840 | sat=&priv->current[priv->sat_item.id_lo-1]; |
---|
841 | switch (type) { |
---|
842 | case attr_sat_prn: |
---|
843 | attr->u.num=sat->prn; |
---|
844 | break; |
---|
845 | case attr_sat_elevation: |
---|
846 | attr->u.num=sat->elevation; |
---|
847 | break; |
---|
848 | case attr_sat_azimuth: |
---|
849 | attr->u.num=sat->azimuth; |
---|
850 | break; |
---|
851 | case attr_sat_snr: |
---|
852 | attr->u.num=sat->snr; |
---|
853 | break; |
---|
854 | default: |
---|
855 | return 0; |
---|
856 | } |
---|
857 | attr->type = type; |
---|
858 | return 1; |
---|
859 | } |
---|
860 | |
---|
861 | static struct item_methods vehicle_wince_sat_methods = { |
---|
862 | NULL, |
---|
863 | NULL, |
---|
864 | NULL, |
---|
865 | vehicle_wince_sat_attr_get, |
---|
866 | NULL, |
---|
867 | NULL, |
---|
868 | NULL, |
---|
869 | NULL, |
---|
870 | }; |
---|
871 | |
---|
872 | struct vehicle_methods vehicle_wince_methods = { |
---|
873 | vehicle_wince_destroy, |
---|
874 | vehicle_wince_position_attr_get, |
---|
875 | NULL, |
---|
876 | }; |
---|
877 | |
---|
878 | static struct vehicle_priv * |
---|
879 | vehicle_wince_new(struct vehicle_methods |
---|
880 | *meth, struct callback_list |
---|
881 | *cbl, struct attr **attrs) |
---|
882 | { |
---|
883 | struct vehicle_priv *ret; |
---|
884 | struct attr *source; |
---|
885 | struct attr *time; |
---|
886 | struct attr *on_eof; |
---|
887 | struct attr *baudrate; |
---|
888 | struct attr *checksum_ignore; |
---|
889 | struct attr *handle_bluetooth; |
---|
890 | char *cp; |
---|
891 | |
---|
892 | dbg(1, "enter\n"); |
---|
893 | source = attr_search(attrs, NULL, attr_source); |
---|
894 | ret = g_new0(struct vehicle_priv, 1); |
---|
895 | ret->fd = -1; |
---|
896 | ret->cbl = cbl; |
---|
897 | |
---|
898 | ret->file_type = file_type_device; |
---|
899 | cp = strchr(source->u.str,':'); |
---|
900 | if (cp) |
---|
901 | { |
---|
902 | if ( strncmp(source->u.str, "file", 4) == 0 ) |
---|
903 | ret->file_type = file_type_file; |
---|
904 | cp++; |
---|
905 | } |
---|
906 | else |
---|
907 | cp = source->u.str; |
---|
908 | ret->source = g_strdup(cp); |
---|
909 | ret->buffer = g_malloc(buffer_size); |
---|
910 | ret->time=1000; |
---|
911 | ret->baudrate=0; // do not change the rate if not configured |
---|
912 | |
---|
913 | time = attr_search(attrs, NULL, attr_time); |
---|
914 | if (time) |
---|
915 | ret->time=time->u.num; |
---|
916 | baudrate = attr_search(attrs, NULL, attr_baudrate); |
---|
917 | if (baudrate) { |
---|
918 | ret->baudrate = baudrate->u.num; |
---|
919 | } |
---|
920 | checksum_ignore = attr_search(attrs, NULL, attr_checksum_ignore); |
---|
921 | if (checksum_ignore) |
---|
922 | ret->checksum_ignore=checksum_ignore->u.num; |
---|
923 | ret->attrs = attrs; |
---|
924 | on_eof = attr_search(attrs, NULL, attr_on_eof); |
---|
925 | if (on_eof && !g_strcasecmp(on_eof->u.str, "stop")) |
---|
926 | ret->on_eof=1; |
---|
927 | if (on_eof && !g_strcasecmp(on_eof->u.str, "exit")) |
---|
928 | ret->on_eof=2; |
---|
929 | dbg(0,"on_eof=%d\n", ret->on_eof); |
---|
930 | *meth = vehicle_wince_methods; |
---|
931 | ret->priv_cbl = callback_list_new(); |
---|
932 | callback_list_add(ret->priv_cbl, callback_new_1(callback_cast(vehicle_wince_io), ret)); |
---|
933 | ret->sat_item.type=type_position_sat; |
---|
934 | ret->sat_item.id_hi=ret->sat_item.id_lo=0; |
---|
935 | ret->sat_item.priv_data=ret; |
---|
936 | ret->sat_item.meth=&vehicle_wince_sat_methods; |
---|
937 | |
---|
938 | ret->read_buffer = g_malloc(buffer_size); |
---|
939 | |
---|
940 | handle_bluetooth = attr_search(attrs, NULL, attr_bluetooth); |
---|
941 | if ( handle_bluetooth && handle_bluetooth->u.num == 1 ) |
---|
942 | initBth(ret); |
---|
943 | |
---|
944 | if (vehicle_wince_open(ret)) { |
---|
945 | vehicle_wince_enable_watch(ret); |
---|
946 | return ret; |
---|
947 | } |
---|
948 | dbg(0, "Failed to open '%s'\n", ret->source); |
---|
949 | vehicle_wince_destroy(ret); |
---|
950 | return NULL; |
---|
951 | } |
---|
952 | |
---|
953 | void |
---|
954 | plugin_init(void) |
---|
955 | { |
---|
956 | dbg(1, "enter\n"); |
---|
957 | plugin_register_vehicle_type("wince", vehicle_wince_new); |
---|
958 | plugin_register_vehicle_type("file", vehicle_wince_new); |
---|
959 | } |
---|