OpenCPN Partial API docs
Loading...
Searching...
No Matches
garmin_protocol_mgr.cpp
Go to the documentation of this file.
1/***************************************************************************
2 * Copyright (C) 2010 by David S. Register *
3 * *
4 * This program is free software; you can redistribute it and/or modify *
5 * it under the terms of the GNU General Public License as published by *
6 * the Free Software Foundation; either version 2 of the License, or *
7 * (at your option) any later version. *
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, see <https://www.gnu.org/licenses/>. *
16 **************************************************************************/
17
27#include <vector>
28
29#include <stdlib.h>
30#include <math.h>
31#include <time.h>
32
33#ifndef _MSC_VER
34#include <arpa/inet.h>
35#include <netinet/tcp.h>
36#endif
37
38#ifdef _WIN32
39#include <winsock2.h>
40#include <windows.h>
41#include <winioctl.h>
42#include <initguid.h>
43#include <setupapi.h>
44#endif
45
46#include <wx/wxprec.h>
47#ifndef WX_PRECOMP
48#include <wx/wx.h>
49#endif
50
51#include <wx/datetime.h>
52#include <wx/event.h>
53#include <wx/log.h>
54#include <wx/string.h>
55#include <wx/tokenzr.h>
56#include <wx/utils.h>
57
59#include "model/config_vars.h"
60#include "config.h"
61#include "model/garmin_wrapper.h"
64#include "nmea0183.h"
65
66#ifdef __ANDROID__
67#include "androidUTIL.h"
68#endif
69
70#if !defined(NAN)
71static const long long lNaN = 0xfff8000000000000;
72#define NAN (*(double *)&lNaN)
73#endif
74
75#define N_DOG_TIMEOUT 5
76
77#ifdef __WXMSW__
78// {2C9C45C2-8E7D-4C08-A12D-816BBAE722C0}
79DEFINE_GUID(GARMIN_GUID1, 0x2c9c45c2L, 0x8e7d, 0x4c08, 0xa1, 0x2d, 0x81, 0x6b,
80 0xba, 0xe7, 0x22, 0xc0);
81#endif
82
83//----------------------------------------------------------------------------
84// Garmin Device Management
85// Handle USB and Serial Port Garmin PVT protocol data interface.
86//----------------------------------------------------------------------------
87
88#ifdef __WXMSW__
89BOOL IsUserAdmin(VOID)
90/*++
91 * Routine Description: This routine returns TRUE if the caller's
92 * process is a member of the Administrators local group. Caller is
93 * NOT expected to be impersonating anyone and is expected to be able to open
94 * its own process and process token. Arguments: None. Return Value: TRUE -
95 * Caller has Administrators local group. FALSE - Caller does not have
96 * Administrators local group. --
97 */
98{
99 BOOL b;
100 SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
101 PSID AdministratorsGroup;
102 b = AllocateAndInitializeSid(&NtAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
103 DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0,
104 &AdministratorsGroup);
105 if (b) {
106 if (!CheckTokenMembership(NULL, AdministratorsGroup, &b)) {
107 b = FALSE;
108 }
109 FreeSid(AdministratorsGroup);
110 }
111
112 return (b);
113}
114
115#endif
116
117BEGIN_EVENT_TABLE(GarminProtocolHandler, wxEvtHandler)
118EVT_TIMER(TIMER_GARMIN1, GarminProtocolHandler::OnTimerGarmin1)
119END_EVENT_TABLE()
120
122 SendMsgFunc send_msg_func,
123 bool bsel_usb) {
124 // m_pparent = parent;
125 m_send_msg_func = send_msg_func;
126 m_garmin_serial_thread = NULL;
127 m_garmin_usb_thread = NULL;
128 m_bOK = false;
129 m_busb = bsel_usb;
130
131 // Connect(wxEVT_OCPN_GARMIN,
132 // (wxObjectEventFunction)(wxEventFunction)&GarminProtocolHandler::OnEvtGarmin);
133
134 char pvt_on[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 49, 0};
135
136 char pvt_off[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 50, 0};
137
138#ifdef __WXMSW__
139 if (m_busb) {
140 m_usb_handle = INVALID_HANDLE_VALUE;
141
142 m_bneed_int_reset = true;
143 m_receive_state = rs_fromintr;
144 m_ndelay = 0;
145
146 wxLogMessage("Searching for Garmin DeviceInterface and Device...");
147
148 if (!FindGarminDeviceInterface()) {
149 wxLogMessage(" Find:Is the Garmin USB driver installed?");
150 } else {
151 if (!ResetGarminUSBDriver())
152 wxLogMessage(" Reset:Is the Garmin USB Device plugged in?");
153 }
154 }
155#endif
156
157 // Not using USB, so try a Garmin port open and device ident
158 if (!m_busb) {
159 m_port = port;
160
161 // Start handler thread
162 m_garmin_serial_thread =
163 new GARMIN_Serial_Thread(this, m_send_msg_func, m_port);
164
165 m_Thread_run_flag = 1;
166 m_garmin_serial_thread->Run();
167 }
168
169 TimerGarmin1.SetOwner(this, TIMER_GARMIN1);
170 TimerGarmin1.Start(100);
171}
172
173GarminProtocolHandler::~GarminProtocolHandler() {}
174
175void GarminProtocolHandler::Close() {
176 TimerGarmin1.Stop();
177
178 StopIOThread(true);
179 StopSerialThread();
180}
181
182void GarminProtocolHandler::StopSerialThread() {
183 if (m_garmin_serial_thread) {
184 wxLogMessage("Stopping Garmin Serial thread");
185 m_Thread_run_flag = 0;
186
187 int tsec = 5;
188 while ((m_Thread_run_flag >= 0) && (tsec--)) {
189 wxSleep(1);
190 }
191
192 wxString msg;
193 if (m_Thread_run_flag < 0)
194 msg.Printf("Stopped in %d sec.", 5 - tsec);
195 else
196 msg.Printf("Not Stopped after 5 sec.");
197 wxLogMessage(msg);
198 }
199
200 m_garmin_serial_thread = NULL;
201}
202
203void GarminProtocolHandler::StopIOThread(bool b_pause) {
204 if (b_pause) TimerGarmin1.Stop();
205
206 if (m_garmin_usb_thread) {
207 wxLogMessage("Stopping Garmin USB thread");
208 m_Thread_run_flag = 0;
209
210 int tsec = 5;
211 while ((m_Thread_run_flag >= 0) && (tsec--)) {
212 wxSleep(1);
213 }
214
215 wxString msg;
216 if (m_Thread_run_flag < 0)
217 msg.Printf("Stopped in %d sec.", 5 - tsec);
218 else
219 msg.Printf("Not Stopped after 5 sec.");
220 wxLogMessage(msg);
221 }
222
223 m_garmin_usb_thread = NULL;
224
225#ifdef __WXMSW__
226 if (m_busb && (m_usb_handle != INVALID_HANDLE_VALUE))
227 CloseHandle(m_usb_handle);
228 m_usb_handle = INVALID_HANDLE_VALUE;
229#endif
230
231 m_ndelay = 30; // Fix delay for next restart
232}
233
234void GarminProtocolHandler::RestartIOThread() {
235 wxLogMessage("Restarting Garmin I/O thread");
236 TimerGarmin1.Start(1000);
237}
238
239void GarminProtocolHandler::OnTimerGarmin1(wxTimerEvent &event) {
240 char pvt_on[14] = {20, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 49, 0};
241
242 TimerGarmin1.Stop();
243
244 if (m_busb) {
245#ifdef __WXMSW__
246 // Try to open the Garmin USB device
247 if (INVALID_HANDLE_VALUE == m_usb_handle) {
248 if (INVALID_HANDLE_VALUE != garmin_usb_start()) {
249 // Send out a request for Garmin PVT data
250 m_receive_state = rs_fromintr;
251 gusb_cmd_send((const garmin_usb_packet *)pvt_on, sizeof(pvt_on));
252
253 // Start the pump
254 m_garmin_usb_thread = new GARMIN_USB_Thread(
255 this, m_send_msg_func, (wxIntPtr)m_usb_handle, m_max_tx_size);
256 m_Thread_run_flag = 1;
257 m_garmin_usb_thread->Run();
258 }
259 }
260#endif
261 }
262
263 TimerGarmin1.Start(1000);
264}
265
266#ifdef __WXMSW__
267bool GarminProtocolHandler::ResetGarminUSBDriver() {
268 OSVERSIONINFO version_info;
269 version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
270
271 if (GetVersionEx(&version_info)) {
272 if (version_info.dwMajorVersion > 5) {
273 if (!IsUserAdmin()) {
274 wxLogMessage(
275 " GarminUSBDriver Reset skipped, requires elevated "
276 "privileges on Vista and later....");
277 return true;
278 }
279 }
280 }
281
282 HDEVINFO devs;
283 SP_DEVINFO_DATA devInfo;
284 SP_PROPCHANGE_PARAMS pchange;
285
286 devs = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
287 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
288 if (devs == INVALID_HANDLE_VALUE) return false;
289
290 devInfo.cbSize = sizeof(devInfo);
291 if (!SetupDiEnumDeviceInfo(devs, 0, &devInfo)) {
292 wxLogMessage(" GarminUSBDriver Reset0 failed...");
293 return false;
294 }
295
296 pchange.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER);
297 pchange.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE;
298 pchange.StateChange = DICS_PROPCHANGE;
299 pchange.Scope = DICS_FLAG_CONFIGSPECIFIC;
300 pchange.HwProfile = 0;
301
302 if (!SetupDiSetClassInstallParams(devs, &devInfo, &pchange.ClassInstallHeader,
303 sizeof(pchange))) {
304 wxLogMessage(" GarminUSBDriver Reset1 failed...");
305 return false;
306 }
307
308 if (!SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, devs, &devInfo)) {
309 wxLogMessage(" GarminUSBDriver Reset2 failed...");
310 return false;
311 }
312
313 wxLogMessage("GarminUSBDriver Reset succeeded.");
314
315 return true;
316}
317
318bool GarminProtocolHandler::
319 FindGarminDeviceInterface() { // Search for a useable Garmin Device
320 // Interface Class
321
322 HDEVINFO hdevinfo;
323 SP_DEVINFO_DATA devInfo;
324
325 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
326 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
327
328 if (hdevinfo != INVALID_HANDLE_VALUE) {
329 devInfo.cbSize = sizeof(devInfo);
330 if (!SetupDiEnumDeviceInfo(hdevinfo, 0, &devInfo)) {
331 return false;
332 }
333 }
334
335 return true;
336}
337
338bool GarminProtocolHandler::IsGarminPlugged() {
339 DWORD size = 0;
340
341 HDEVINFO hdevinfo;
342 SP_DEVICE_INTERFACE_DATA infodata;
343
344 // Search for the Garmin Device Interface Class
345 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
346 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
347
348 if (hdevinfo == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
349
350 infodata.cbSize = sizeof(infodata);
351
352 bool bgarmin_unit_found =
353 (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, (GUID *)&GARMIN_GUID1, 0,
354 &infodata) != 0);
355
356 if (!bgarmin_unit_found) return false;
357
358 PSP_INTERFACE_DEVICE_DETAIL_DATA pdd = NULL;
359 SP_DEVINFO_DATA devinfo;
360
361 SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, NULL, 0, &size, NULL);
362
363 pdd = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(size);
364 pdd->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
365
366 devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
367 if (!SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, pdd, size, NULL,
368 &devinfo)) {
369 free(pdd);
370 return false;
371 }
372
373 free(pdd);
374
375 return true;
376}
377
378HANDLE GarminProtocolHandler::garmin_usb_start() {
379 DWORD size = 0;
380
381 HDEVINFO hdevinfo;
382 SP_DEVICE_INTERFACE_DATA infodata;
383
384 // Search for the Garmin Device Interface Class
385 hdevinfo = SetupDiGetClassDevs((GUID *)&GARMIN_GUID1, NULL, NULL,
386 DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
387
388 if (hdevinfo == INVALID_HANDLE_VALUE) return INVALID_HANDLE_VALUE;
389
390 infodata.cbSize = sizeof(infodata);
391
392 bool bgarmin_unit_found =
393 (SetupDiEnumDeviceInterfaces(hdevinfo, NULL, (GUID *)&GARMIN_GUID1, 0,
394 &infodata) != 0);
395
396 if (!bgarmin_unit_found) return INVALID_HANDLE_VALUE;
397
398 wxLogMessage("Garmin USB Device Found");
399
400 if ((m_usb_handle == INVALID_HANDLE_VALUE) || (m_usb_handle == 0)) {
401 PSP_INTERFACE_DEVICE_DETAIL_DATA pdd = NULL;
402 SP_DEVINFO_DATA devinfo;
403
404 SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, NULL, 0, &size, NULL);
405
406 pdd = (PSP_INTERFACE_DEVICE_DETAIL_DATA)malloc(size);
407 pdd->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);
408
409 devinfo.cbSize = sizeof(SP_DEVINFO_DATA);
410 if (!SetupDiGetDeviceInterfaceDetail(hdevinfo, &infodata, pdd, size, NULL,
411 &devinfo)) {
412 wxLogMessage(
413 " SetupDiGetDeviceInterfaceDetail failed for Garmin Device...");
414 free(pdd);
415 return INVALID_HANDLE_VALUE;
416 }
417
418 /* Whew. All that just to get something we can open... */
419 // wxString msg;
420 // msg.Printf(_T("Windows GUID for interface is
421 // %s"),pdd->DevicePath); wxLogMessage(msg);
422
423 if (m_bneed_int_reset) {
424 ResetGarminUSBDriver();
425 m_bneed_int_reset = false;
426 }
427
428 m_usb_handle = CreateFile(pdd->DevicePath, GENERIC_READ | GENERIC_WRITE, 0,
429 NULL, OPEN_EXISTING, 0, NULL);
430
431 if (m_usb_handle == INVALID_HANDLE_VALUE) {
432 wxString msg;
433 msg.Printf(" (usb) CreateFile on '%s' failed", pdd->DevicePath);
434 wxLogMessage(msg);
435 }
436
437 /*
438 DEV_BROADCAST_HANDLE filterData;
439 filterData.dbch_size = sizeof(DEV_BROADCAST_HANDLE);
440 filterData.dbch_devicetype = DBT_DEVTYP_HANDLE;
441 filterData.dbch_reserved = 0;
442 filterData.dbch_handle = m_usb_handle; // file handle used in
443 call to RegisterDeviceNotification filterData.dbch_hdevnotify = 0; //
444 returned from RegisterDeviceNotification
445
446 HDEVNOTIFY m_hDevNotify = RegisterDeviceNotification( GetHWND(),
447 &filterData, DEVICE_NOTIFY_WINDOW_HANDLE);
448 */
449
450 free(pdd);
451 }
452
453 m_max_tx_size = 0;
454
455 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_BULK_OUT_PACKET_SIZE,
456 NULL, 0, &m_max_tx_size, GARMIN_USB_INTERRUPT_DATA_SIZE,
457 &size, NULL)) {
458 wxLogMessage(" Couldn't get Garmin USB packet size.");
459 CloseHandle(m_usb_handle);
460 m_usb_handle = INVALID_HANDLE_VALUE;
461 return INVALID_HANDLE_VALUE;
462 }
463
464 if (!gusb_syncup()) {
465 CloseHandle(m_usb_handle);
466 m_usb_handle = INVALID_HANDLE_VALUE;
467 }
468
469 return m_usb_handle;
470}
471
472bool GarminProtocolHandler::gusb_syncup() {
473 static int unit_number;
474 static const char oinit[12] = {0, 0, 0, 0, GUSB_SESSION_START, 0, 0, 0,
475 0, 0, 0, 0};
476 garmin_usb_packet iresp;
477 int i;
478
479 /*
480 * This is our first communication with the unit.
481 */
482
483 m_receive_state = rs_fromintr;
484
485 for (i = 0; i < 25; i++) {
486 le_write16(&iresp.gusb_pkt.pkt_id[0], 0);
487 le_write32(&iresp.gusb_pkt.datasz[0], 0);
488 le_write32(&iresp.gusb_pkt.databuf[0], 0);
489
490 if (gusb_cmd_send((const garmin_usb_packet *)oinit, sizeof(oinit))) {
491 gusb_cmd_get(&iresp, sizeof(iresp));
492
493 if ((le_read16(iresp.gusb_pkt.pkt_id) == GUSB_SESSION_ACK) &&
494 (le_read32(iresp.gusb_pkt.datasz) == 4)) {
495 // unsigned serial_number =
496 // le_read32(iresp.gusb_pkt.databuf);
497 // garmin_unit_info[unit_number].serial_number =
498 // serial_number;
499 // gusb_id_unit(&garmin_unit_info[unit_number]);
500
501 unit_number++;
502
503 wxLogMessage("Successful Garmin USB syncup.");
504 return true;
505 ;
506 }
507 }
508 }
509 wxLogMessage(" Unable to establish Garmin USB syncup.");
510 return false;
511}
512
513int GarminProtocolHandler::gusb_cmd_send(const garmin_usb_packet *opkt,
514 size_t sz) {
515 unsigned int rv;
516
517 unsigned char *obuf = (unsigned char *)&opkt->dbuf[0];
518
519 rv = gusb_win_send(opkt, sz);
520
521 /*
522 * Recursion, when used in a disciplined way, can be our friend.
523 *
524 * The Garmin protocol requires that packets that are exactly
525 * a multiple of the max tx size be followed by a zero length
526 * packet. Do that here so we can see it in debugging traces.
527 */
528
529 if (sz && !(sz % m_max_tx_size)) {
530 wxLogMessage("win_send_call1");
531 gusb_win_send(opkt, 0);
532 wxLogMessage("win_send_ret1");
533 }
534
535 return (rv);
536}
537
538int GarminProtocolHandler::gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) {
539 int rv = 0;
540 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
541 int orig_receive_state;
542top:
543 orig_receive_state = m_receive_state;
544 switch (m_receive_state) {
545 case rs_fromintr:
546 rv = gusb_win_get(ibuf, sz);
547 break;
548 case rs_frombulk:
549 rv = gusb_win_get_bulk(ibuf, sz);
550 break;
551 }
552
553 /* Adjust internal state and retry the read */
554 if ((rv > 0) && (ibuf->gusb_pkt.pkt_id[0] == GUSB_REQUEST_BULK)) {
555 m_receive_state = rs_frombulk;
556 goto top;
557 }
558 /*
559 * If we were reading from the bulk pipe and we just got
560 * a zero request, adjust our internal state.
561 * It's tempting to retry the read here to hide this "stray"
562 * packet from our callers, but that only works when you know
563 * there's another packet coming. That works in every case
564 * except the A000 discovery sequence.
565 */
566 if ((m_receive_state == rs_frombulk) && (rv <= 0)) {
567 m_receive_state = rs_fromintr;
568 }
569
570 return rv;
571}
572
573int GarminProtocolHandler::gusb_win_get(garmin_usb_packet *ibuf, size_t sz) {
574 DWORD rxed = GARMIN_USB_INTERRUPT_DATA_SIZE;
575 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
576 int tsz = 0;
577
578 while (sz) {
579 /* The driver wrongly (IMO) rejects reads smaller than
580 * GARMIN_USB_INTERRUPT_DATA_SIZE
581 */
582 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_INTERRUPT_IN, NULL, 0,
583 buf, GARMIN_USB_INTERRUPT_DATA_SIZE, &rxed, NULL)) {
584 // GPS_Serial_Error("Ioctl");
585 // fatal("ioctl\n");
586 }
587
588 buf += rxed;
589 sz -= rxed;
590 tsz += rxed;
591 if (rxed < GARMIN_USB_INTERRUPT_DATA_SIZE) break;
592 }
593 return tsz;
594}
595
596int GarminProtocolHandler::gusb_win_get_bulk(garmin_usb_packet *ibuf,
597 size_t sz) {
598 int n;
599 DWORD rsz;
600 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
601
602 n = ReadFile(m_usb_handle, buf, sz, &rsz, NULL);
603
604 return rsz;
605}
606
607int GarminProtocolHandler::gusb_win_send(const garmin_usb_packet *opkt,
608 size_t sz) {
609 DWORD rsz;
610 unsigned char *obuf = (unsigned char *)&opkt->dbuf[0];
611
612 /* The spec warns us about making writes an exact multiple
613 * of the packet size, but isn't clear whether we can issue
614 * data in a single call to WriteFile if it spans buffers.
615 */
616 WriteFile(m_usb_handle, obuf, sz, &rsz, NULL);
617 int err = GetLastError();
618
619 // if (rsz != sz)
620 // fatal ("Error sending %d bytes. Successfully sent %ld\n", sz,
621 // rsz);
622
623 return rsz;
624}
625
626/*
627WXLRESULT GarminProtocolHandler::MSWWindowProc(WXUINT message, WXWPARAM wParam,
628WXLPARAM lParam)
629{
630 // did we process the message?
631 bool processed = false;
632
633 // the return value
634 bool rc;
635 PDEV_BROADCAST_HDR pDBHdr;
636 PDEV_BROADCAST_HANDLE pDBHandle;
637
638 // for most messages we should return 0 when we do process the message
639 rc = 0;
640
641 switch ( message )
642 {
643 case WM_DEVICECHANGE:
644 switch (wParam)
645 {
646 case DBT_DEVICEREMOVEPENDING:
647 case DBT_DEVICEREMOVECOMPLETE:
648 pDBHdr = (PDEV_BROADCAST_HDR) lParam;
649 switch (pDBHdr->dbch_devicetype)
650 case DBT_DEVTYP_HANDLE:
651 // A Device has been removed
652 // Stop the IO thread and close open handle to
653device
654
655 pDBHandle = (PDEV_BROADCAST_HANDLE) pDBHdr;
656 HANDLE target_handle = pDBHandle->dbch_handle;
657
658 wxLogMessage("Garmin USB Device Removed");
659 StopIOThread(false);
660 m_bneed_int_reset = true;
661 processed = true;
662 break;
663 }
664
665 break;
666
667 }
668
669 if ( !processed )
670 {
671 rc = (MSWDefWindowProc(message, wParam, lParam) != 0);
672 }
673
674 return rc;
675}
676*/
677#endif
678
680
681//-------------------------------------------------------------------------------------------------------------
682//
683// Garmin Serial Port Worker Thread
684//
685// This thread manages reading the positioning data stream from the declared
686// Garmin GRMN Mode serial device
687//
688//-------------------------------------------------------------------------------------------------------------
689GARMIN_Serial_Thread::GARMIN_Serial_Thread(GarminProtocolHandler *parent,
690 SendMsgFunc send_msg_func,
691 wxString port) {
692 m_parent = parent; // This thread's immediate "parent"
693 m_send_msg_func = send_msg_func;
694 m_port = port;
695
696 Create();
697}
698
699GARMIN_Serial_Thread::~GARMIN_Serial_Thread() {}
700
701// Entry Point
702void *GARMIN_Serial_Thread::Entry() {
703 // m_parent->SetSecThreadActive(); // I am alive
704 m_bdetected = false;
705 m_bconnected = false;
706
707 bool not_done = true;
708 wxDateTime last_rx_time;
709
710#ifdef USE_GARMINHOST
711 // The main loop
712
713 while ((not_done) && (m_parent->m_Thread_run_flag > 0)) {
714 if (TestDestroy()) {
715 not_done = false; // smooth exit
716 goto thread_exit_2;
717 }
718
719 while (!m_bdetected) {
720 // Try to init the port once
721 int v_init = Garmin_GPS_Init(m_port);
722 if (v_init < 0) { // Open failed, so sleep and try again
723 for (int i = 0; i < 4; i++) {
724 wxSleep(1);
725 if (TestDestroy()) goto thread_exit;
726 if (!m_parent->m_Thread_run_flag) goto thread_exit;
727 }
728 } else
729 m_bdetected = true;
730 } // while not detected
731
732 // Detected OK
733
734 // Start PVT packet transmission
735 if (!m_bconnected) {
736 if (!Garmin_GPS_PVT_On(m_port)) {
737 m_bdetected = false; // error, would not accept PVT On
738 m_bconnected = false;
739 } else
740 m_bconnected = true;
741 }
742
743 if (m_bconnected) {
744 D800_Pvt_Data_Type_Aligned *ppvt = &mypvt;
745 int ret = Garmin_GPS_GetPVT(&ppvt);
746 if (ret > 0) {
747 if ((mypvt.fix) >= 2 && (mypvt.fix <= 5)) {
748 // Synthesize an NMEA GMRMC message
749 SENTENCE snt;
750 NMEA0183 oNMEA0183(NmeaCtxFactory());
751 oNMEA0183.TalkerID = "GM";
752
753 if (mypvt.lat < 0.)
754 oNMEA0183.Rmc.Position.Latitude.Set(-mypvt.lat, "S");
755 else
756 oNMEA0183.Rmc.Position.Latitude.Set(mypvt.lat, "N");
757
758 if (mypvt.lon < 0.)
759 oNMEA0183.Rmc.Position.Longitude.Set(-mypvt.lon, "W");
760 else
761 oNMEA0183.Rmc.Position.Longitude.Set(mypvt.lon, "E");
762
763 /* speed over ground */
764 double sog =
765 sqrt(mypvt.east * mypvt.east + mypvt.north * mypvt.north) * 3.6 /
766 1.852;
767 oNMEA0183.Rmc.SpeedOverGroundKnots = sog;
768
769 /* course over ground */
770 double course = atan2(mypvt.east, mypvt.north);
771 if (course < 0) course += 2 * PI;
772 double cog = course * 180 / PI;
773 oNMEA0183.Rmc.TrackMadeGoodDegreesTrue = cog;
774
775 oNMEA0183.Rmc.IsDataValid = NTrue;
776
777 oNMEA0183.Rmc.Write(snt);
778
779 // Send message...
780 auto msg = snt.Sentence.ToStdString();
781 m_send_msg_func(std::vector<unsigned char>(msg.begin(), msg.end()));
782
783 last_rx_time = wxDateTime::Now();
784 }
785 } else {
786 wxDateTime now = wxDateTime::Now();
787 if (last_rx_time.IsValid()) {
788 wxTimeSpan delta_time = now - last_rx_time;
789 if (delta_time.GetSeconds() > 5) {
790 m_bdetected = false;
791 m_bconnected = false;
792 Garmin_GPS_ClosePortVerify();
793 }
794 }
795 }
796 }
797 } // the big while...
798
799thread_exit_2:
800
801 Garmin_GPS_PVT_Off(m_port);
802 Garmin_GPS_ClosePortVerify();
803
804 while ((not_done) && (m_parent->m_Thread_run_flag > 0)) {
805 wxSleep(1);
806 if (TestDestroy()) {
807 not_done = false; // smooth exit
808 goto thread_exit;
809 }
810 }
811
812thread_exit:
813
814#endif // #ifdef USE_GARMINHOST
815
816 m_parent->m_Thread_run_flag = -1; // in GarminProtocolHandler
817 return 0;
818}
819
820//-------------------------------------------------------------------------------------------------------------
821// GARMIN_USB_Thread Implementation
822//-------------------------------------------------------------------------------------------------------------
823#if 1
824GARMIN_USB_Thread::GARMIN_USB_Thread(GarminProtocolHandler *parent,
825 SendMsgFunc send_msg_func,
826 unsigned int device_handle,
827 size_t max_tx_size) {
828 m_parent = parent; // This thread's immediate "parent"
829 m_send_msg_func = send_msg_func;
830 m_max_tx_size = max_tx_size;
831
832#ifdef __WXMSW__
833 m_usb_handle = (HANDLE)(device_handle & 0xffff);
834#endif
835
836 Create();
837}
838
839GARMIN_USB_Thread::~GARMIN_USB_Thread() {}
840
841void *GARMIN_USB_Thread::Entry() {
842 garmin_usb_packet iresp = {{0}};
843 int n_short_read = 0;
844 m_receive_state = rs_fromintr;
845
846 // Here comes the big while loop
847 while (m_parent->m_Thread_run_flag > 0) {
848 if (TestDestroy()) goto thread_prexit; // smooth exit
849
850 // Get one packet
851
852 int nr = gusb_cmd_get(&iresp, sizeof(iresp));
853
854 if (iresp.gusb_pkt.pkt_id[0] == GUSB_RESPONSE_SDR) // Satellite Data Record
855 {
856 unsigned char *t = (unsigned char *)&(iresp.gusb_pkt.databuf[0]);
857 for (int i = 0; i < 12; i++) {
858 m_sat_data[i].svid = *t++;
859 m_sat_data[i].snr = ((*t) << 8) + *(t + 1);
860 t += 2;
861 m_sat_data[i].elev = *t++;
862 m_sat_data[i].azmth = ((*t) << 8) + *(t + 1);
863 t += 2;
864 m_sat_data[i].status = *t++;
865 }
866
867 m_nSats = 0;
868 for (int i = 0; i < 12; i++) {
869 if (m_sat_data[i].svid != 255) m_nSats++;
870 }
871
872 // Synthesize an NMEA GMGSV message
873 SENTENCE snt;
874 NMEA0183 oNMEA0183(NmeaCtxFactory());
875 oNMEA0183.TalkerID = "GM";
876 oNMEA0183.Gsv.SatsInView = m_nSats;
877
878 oNMEA0183.Gsv.Write(snt);
879 wxString msg = snt.Sentence;
880
881 // and send message.
882 m_send_msg_func(std::vector<unsigned char>(msg.begin(), msg.end()));
883 }
884
885 if (iresp.gusb_pkt.pkt_id[0] == GUSB_RESPONSE_PVT) // PVT Data Record
886 {
887 D800_Pvt_Data_Type *ppvt =
888 (D800_Pvt_Data_Type *)&(iresp.gusb_pkt.databuf[0]);
889
890 if ((ppvt->fix) >= 2 && (ppvt->fix <= 5)) {
891 // Synthesize an NMEA GMRMC message
892 SENTENCE snt;
893 NMEA0183 oNMEA0183(NmeaCtxFactory());
894 oNMEA0183.TalkerID = "GM";
895
896 if (ppvt->lat < 0.)
897 oNMEA0183.Rmc.Position.Latitude.Set(-ppvt->lat * 180. / PI, "S");
898 else
899 oNMEA0183.Rmc.Position.Latitude.Set(ppvt->lat * 180. / PI, "N");
900
901 if (ppvt->lon < 0.)
902 oNMEA0183.Rmc.Position.Longitude.Set(-ppvt->lon * 180. / PI, "W");
903 else
904 oNMEA0183.Rmc.Position.Longitude.Set(ppvt->lon * 180. / PI, "E");
905
906 /* speed over ground */
907 double sog = sqrt(ppvt->east * ppvt->east + ppvt->north * ppvt->north) *
908 3.6 / 1.852;
909 oNMEA0183.Rmc.SpeedOverGroundKnots = sog;
910
911 /* course over ground */
912 double course = atan2(ppvt->east, ppvt->north);
913 if (course < 0) course += 2 * PI;
914 double cog = course * 180 / PI;
915 oNMEA0183.Rmc.TrackMadeGoodDegreesTrue = cog;
916
917 oNMEA0183.Rmc.IsDataValid = NTrue;
918
919 oNMEA0183.Rmc.Write(snt);
920 wxString msg = snt.Sentence;
921 m_send_msg_func(std::vector<unsigned char>(msg.begin(), msg.end()));
922 }
923 }
924 }
925thread_prexit:
926 m_parent->m_Thread_run_flag = -1;
927 return 0;
928}
929
930int GARMIN_USB_Thread::gusb_cmd_get(garmin_usb_packet *ibuf, size_t sz) {
931 int rv = 0;
932 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
933 int orig_receive_state;
934top:
935 orig_receive_state = m_receive_state;
936 switch (m_receive_state) {
937 case rs_fromintr:
938 rv = gusb_win_get(ibuf, sz);
939 break;
940 case rs_frombulk:
941 rv = gusb_win_get_bulk(ibuf, sz);
942 break;
943 }
944
945 /* Adjust internal state and retry the read */
946 if ((rv > 0) && (ibuf->gusb_pkt.pkt_id[0] == GUSB_REQUEST_BULK)) {
947 m_receive_state = rs_frombulk;
948 goto top;
949 }
950 /*
951 * If we were reading from the bulk pipe and we just got
952 * a zero request, adjust our internal state.
953 * It's tempting to retry the read here to hide this "stray"
954 * packet from our callers, but that only works when you know
955 * there's another packet coming. That works in every case
956 * except the A000 discovery sequence.
957 */
958 if ((m_receive_state == rs_frombulk) && (rv <= 0)) {
959 m_receive_state = rs_fromintr;
960 }
961
962 return rv;
963}
964
965int GARMIN_USB_Thread::gusb_win_get(garmin_usb_packet *ibuf, size_t sz) {
966 int tsz = 0;
967#ifdef __WXMSW__
968 DWORD rxed = GARMIN_USB_INTERRUPT_DATA_SIZE;
969 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
970
971 while (sz) {
972 /* The driver wrongly (IMO) rejects reads smaller than
973 * GARMIN_USB_INTERRUPT_DATA_SIZE
974 */
975 if (!DeviceIoControl(m_usb_handle, IOCTL_GARMIN_USB_INTERRUPT_IN, NULL, 0,
976 buf, GARMIN_USB_INTERRUPT_DATA_SIZE, &rxed, NULL)) {
977 // GPS_Serial_Error("Ioctl");
978 // fatal("ioctl\n");
979 }
980
981 buf += rxed;
982 sz -= rxed;
983 tsz += rxed;
984 if (rxed < GARMIN_USB_INTERRUPT_DATA_SIZE) break;
985 }
986
987#endif
988 return tsz;
989}
990
991int GARMIN_USB_Thread::gusb_win_get_bulk(garmin_usb_packet *ibuf, size_t sz) {
992 int n;
993 int ret_val = 0;
994#ifdef __WXMSW__
995 DWORD rsz;
996 unsigned char *buf = (unsigned char *)&ibuf->dbuf[0];
997
998 n = ReadFile(m_usb_handle, buf, sz, &rsz, NULL);
999 ret_val = rsz;
1000#endif
1001
1002 return ret_val;
1003}
1004#endif
NMEA0183 serial driver.
Global variables stored in configuration file.
NMEA Data Object.
Wrapper for creating an NmeaContext based on global vars.