/** \brief Initialize whatever structures inside the qth_t stucture for later updates. * \param qth the qth data structure to update * *Initial intention of this is to open sockets and ports to gpsd *and other like services to update the qth position. */ gboolean qth_data_update_init(qth_t * qth) { #ifdef HAS_LIBGPS char *port; #endif gboolean retval = FALSE; switch (qth->type) { case QTH_STATIC_TYPE: /* nothing to do. the data never updates */ break; case QTH_GPSD_TYPE: #ifdef HAS_LIBGPS switch (GPSD_API_MAJOR_VERSION) { case 4: #if GPSD_API_MAJOR_VERSION==4 /* open the connection to gpsd and start the stream */ qth->gps_data = g_try_new0(struct gps_data_t, 1); port = g_strdup_printf("%d", qth->gpsd_port); if (gps_open_r(qth->gpsd_server, port, qth->gps_data) == -1) { free(qth->gps_data); qth->gps_data = NULL; sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Could not open gpsd at %s:%d"), __FUNCTION__, qth->gpsd_server, qth->gpsd_port); retval = FALSE; } else { (void)gps_stream(qth->gps_data, WATCH_ENABLE, NULL); retval = TRUE; } g_free(port); #endif break; case 5: #if GPSD_API_MAJOR_VERSION==5 /* open the connection to gpsd and start the stream */ qth->gps_data = g_try_new0(struct gps_data_t, 1); port = g_strdup_printf("%d", qth->gpsd_port); if (gps_open(qth->gpsd_server, port, qth->gps_data) == -1) { free(qth->gps_data); qth->gps_data = NULL; sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Could not open gpsd at %s:%d"), __FUNCTION__, qth->gpsd_server, qth->gpsd_port); retval = FALSE; } else { (void)gps_stream(qth->gps_data, WATCH_ENABLE, NULL); retval = TRUE; } g_free(port); #endif break; default: sat_log_log(SAT_LOG_LEVEL_ERROR, _("%s: Unsupported gpsd api major version (%d)"), __FUNCTION__, GPSD_API_MAJOR_VERSION); return FALSE; break; } #else return FALSE; #endif break; default: break; } return retval; }
int main(int argc, char **argv) { int option, status; char *device = NULL, *devtype = NULL; char *speed = NULL, *control = NULL, *rate = NULL; bool to_binary = false, to_nmea = false, reset = false; bool lowlevel=false, echo=false; struct gps_data_t gpsdata; const struct gps_type_t *forcetype = NULL; const struct gps_type_t **dp; unsigned int timeout = 4; #ifdef ALLOW_CONTROLSEND char cooked[BUFSIZ]; ssize_t cooklen = 0; #endif /* ALLOW_RECONFIGURE */ #define USAGE "usage: gpsctl [-l] [-b | -n | -r] [-D n] [-s speed] [-c rate] [-T timeout] [-V] [-t devtype] [-x control] [-e] <device>\n" while ((option = getopt(argc, argv, "bec:fhlnrs:t:x:D:T:V")) != -1) { switch (option) { case 'b': /* switch to vendor binary mode */ to_binary = true; break; case 'c': #ifdef ALLOW_RECONFIGURE rate = optarg; #else gpsd_report(LOG_ERROR, "cycle-change capability has been conditioned out.\n"); #endif /* ALLOW_RECONFIGURE */ break; case 'x': /* ship specified control string */ #ifdef ALLOW_CONTROLSEND control = optarg; lowlevel = true; if ((cooklen = hex_escapes(cooked, control)) <= 0) { gpsd_report(LOG_ERROR, "invalid escape string (error %d)\n", (int)cooklen); exit(1); } #else gpsd_report(LOG_ERROR, "control_send capability has been conditioned out.\n"); #endif /* ALLOW_CONTROLSEND */ break; case 'e': /* echo specified control string with wrapper */ lowlevel = true; echo = true; break; case 'f': /* force direct access to the device */ lowlevel = true; break; case 'l': /* list known device types */ for (dp = gpsd_drivers; *dp; dp++) { #ifdef ALLOW_RECONFIGURE if ((*dp)->mode_switcher != NULL) (void)fputs("-[bn]\t", stdout); else (void)fputc('\t', stdout); if ((*dp)->speed_switcher != NULL) (void)fputs("-s\t", stdout); else (void)fputc('\t', stdout); if ((*dp)->rate_switcher != NULL) (void)fputs("-c\t", stdout); else (void)fputc('\t', stdout); #endif /* ALLOW_RECONFIGURE */ #ifdef ALLOW_CONTROLSEND if ((*dp)->control_send != NULL) (void)fputs("-x\t", stdout); else (void)fputc('\t', stdout); #endif /* ALLOW_CONTROLSEND */ (void)puts((*dp)->type_name); } exit(0); case 'n': /* switch to NMEA mode */ #ifdef ALLOW_RECONFIGURE to_nmea = true; #else gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n"); #endif /* ALLOW_RECONFIGURE */ break; case 'r': /* force-switch to default mode */ #ifdef ALLOW_RECONFIGURE reset = true; lowlevel = false; /* so we'll abort if the daemon is running */ #else gpsd_report(LOG_ERROR, "reset capability has been conditioned out.\n"); #endif /* ALLOW_RECONFIGURE */ break; case 's': /* change output baud rate */ #ifdef ALLOW_RECONFIGURE speed = optarg; #else gpsd_report(LOG_ERROR, "speed-change capability has been conditioned out.\n"); #endif /* ALLOW_RECONFIGURE */ break; case 't': /* force the device type */ devtype = optarg; break; case 'T': /* set the timeout on packet recognition */ timeout = (unsigned)atoi(optarg); break; case 'D': /* set debugging level */ debuglevel = atoi(optarg); gpsd_hexdump_level = debuglevel; #ifdef CLIENTDEBUG_ENABLE gps_enable_debug(debuglevel, stderr); #endif /* CLIENTDEBUG_ENABLE */ break; case 'V': (void)fprintf(stderr, "gpsctl: version %s (revision %s)\n", VERSION, REVISION); break; case 'h': default: fprintf(stderr, USAGE); break; } } if (optind < argc) device = argv[optind]; if (devtype != NULL) { int matchcount = 0; for (dp = gpsd_drivers; *dp; dp++) { if (strstr((*dp)->type_name, devtype) != NULL) { forcetype = *dp; matchcount++; } } if (matchcount == 0) gpsd_report(LOG_ERROR, "no driver type name matches '%s'.\n", devtype); else if (matchcount == 1) { assert(forcetype != NULL); gpsd_report(LOG_PROG, "%s driver selected.\n", forcetype->type_name); } else { forcetype = NULL; gpsd_report(LOG_ERROR, "%d driver type names match '%s'.\n", matchcount, devtype); } } if ((int)to_nmea + (int)to_binary + (int)reset > 1) { gpsd_report(LOG_ERROR, "make up your mind, would you?\n"); exit(0); } (void) signal(SIGINT, onsig); (void) signal(SIGTERM, onsig); (void) signal(SIGQUIT, onsig); /*@-nullpass@*/ /* someday, add null annotation to the gpsopen_r() params */ if (!lowlevel) { /* Try to open the stream to gpsd. */ if (gps_open_r(NULL, NULL, &gpsdata) != 0) { gpsd_report(LOG_ERROR, "no gpsd running or network error: %s.\n", netlib_errstr(errno)); lowlevel = true; } } /*@-nullpass@*/ /* ^ someday, add out annotation to the gpspoll() param and remove */ if (!lowlevel) { /* OK, there's a daemon instance running. Do things the easy way */ struct devconfig_t *devlistp; (void)gps_read(&gpsdata); if ((gpsdata.set & DEVICELIST_SET) != 0) { gpsd_report(LOG_ERROR, "no VERSION response received; update your gpsd.\n"); (void)gps_close(&gpsdata); exit(1); } (void)gps_query(&gpsdata, "?DEVICES;\n"); if ((gpsdata.set & DEVICELIST_SET) == 0) { gpsd_report(LOG_ERROR, "no DEVICES response received.\n"); (void)gps_close(&gpsdata); exit(1); } if (gpsdata.devices.ndevices == 0) { gpsd_report(LOG_ERROR, "no devices connected.\n"); (void)gps_close(&gpsdata); exit(1); } else if (gpsdata.devices.ndevices > 1 && device == NULL) { gpsd_report(LOG_ERROR, "multiple devices and no device specified.\n"); (void)gps_close(&gpsdata); exit(1); } gpsd_report(LOG_PROG,"%d device(s) found.\n",gpsdata.devices.ndevices); if (gpsdata.devices.ndevices == 1) { devlistp = &gpsdata.devices.list[0]; device = devlistp->path; } else { int i; assert(device != NULL); for (i = 0; i < gpsdata.devices.ndevices; i++) if (strcmp(device, gpsdata.devices.list[i].path) == 0) goto foundit; gpsd_report(LOG_ERROR, "specified device not found.\n"); (void)gps_close(&gpsdata); exit(1); foundit: devlistp = &gpsdata.devices.list[i]; } /* if no control operation was specified, just ID the device */ if (speed==NULL && rate == NULL && !to_nmea && !to_binary && !reset) { gpsd_report(LOG_SHOUT, "%s identified as %s at %d\n", devlistp->path, devlistp->driver, devlistp->baudrate); exit(0); } status = 0; #ifdef ALLOW_RECONFIGURE if (reset) { gpsd_report(LOG_PROG, "cannot reset with gpsd running.\n"); exit(0); } /*@-boolops@*/ if (to_nmea) { (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"native\":0}\r\n", device); if ((gpsdata.set & ERROR_SET) || (gpsdata.dev.driver_mode != MODE_NMEA)) { gpsd_report(LOG_ERROR, "%s mode change to NMEA failed\n", gpsdata.dev.path); status = 1; } else gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path); } else if (to_binary) { (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"native\":1}\r\n", device); if ((gpsdata.set & ERROR_SET) || (gpsdata.dev.driver_mode != MODE_BINARY)) { gpsd_report(LOG_ERROR, "%s mode change to native mode failed\n", gpsdata.dev.path); status = 1; } else gpsd_report(LOG_PROG, "%s mode change succeeded\n", gpsdata.dev.path); } /*@+boolops@*/ if (speed != NULL) { char parity = 'N'; char stopbits = '1'; if (strchr(speed, ':') == NULL) (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"bps\":%s}\r\n", device, speed); else { char *modespec = strchr(speed, ':'); /*@ +charint @*/ status = 0; if (modespec!=NULL) { *modespec = '\0'; if (strchr("78", *++modespec) == NULL) { gpsd_report(LOG_ERROR, "No support for that word lengths.\n"); status = 1; } parity = *++modespec; if (strchr("NOE", parity) == NULL) { gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity); status = 1; } stopbits = *++modespec; if (strchr("12", stopbits) == NULL) { gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n"); status = 1; } } if (status == 0) (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"bps\":%s,\"parity\":\"%c\",\"stopbits\":%c}\r\n", device, speed, parity, stopbits); } if (atoi(speed) != (int)gpsdata.dev.baudrate) { gpsd_report(LOG_ERROR, "%s driver won't support %s%c%c\n", gpsdata.dev.path, speed, parity, stopbits); status = 1; } else gpsd_report(LOG_PROG, "%s change to %s%c%c succeeded\n", gpsdata.dev.path, speed, parity, stopbits); } if (rate != NULL) { (void)gps_query(&gpsdata, "?DEVICE={\"path\":\"%s\",\"cycle\":%s}\n", device, rate); } #endif /* ALLOW_RECONFIGURE */ (void)gps_close(&gpsdata); exit(status); #ifdef ALLOW_RECONFIGURE } else if (reset) { /* hard reset will go through lower-level operations */ const int speeds[] = {2400, 4800, 9600, 19200, 38400, 57600, 115200}; static struct gps_context_t context; /* start it zeroed */ static struct gps_device_t session; /* zero this too */ int i; if (device == NULL || forcetype == NULL) { gpsd_report(LOG_ERROR, "device and type must be specified for the reset operation.\n"); exit(1); } /*@ -mustfreeonly -immediatetrans @*/ session.context = &context; gpsd_tty_init(&session); (void)strlcpy(session.gpsdata.dev.path, device, sizeof(session.gpsdata.dev.path)); session.device_type = forcetype; (void)gpsd_open(&session); (void)gpsd_set_raw(&session); (void)session.device_type->speed_switcher(&session, 4800, 'N', 1); (void)tcdrain(session.gpsdata.gps_fd); for(i = 0; i < (int)(sizeof(speeds) / sizeof(speeds[0])); i++) { (void)gpsd_set_speed(&session, speeds[i], 'N', 1); (void)session.device_type->speed_switcher(&session, 4800, 'N', 1); (void)tcdrain(session.gpsdata.gps_fd); } gpsd_set_speed(&session, 4800, 'N', 1); for (i = 0; i < 3; i++) if (session.device_type->mode_switcher) session.device_type->mode_switcher(&session, MODE_NMEA); gpsd_wrap(&session); exit(0); /*@ +mustfreeonly +immediatetrans @*/ #endif /* ALLOW_RECONFIGURE */ } else { /* access to the daemon failed, use the low-level facilities */ static struct gps_context_t context; /* start it zeroed */ static struct gps_device_t session; /* zero this too */ /*@ -mustfreeonly -immediatetrans @*/ session.context = &context; /* in case gps_init isn't called */ if (echo) context.readonly = true; (void) alarm(timeout); (void) signal(SIGALRM, onsig); /* * Unless the user has forced a type and only wants to see the * string (not send it) we now need to try to open the device * and find out what is actually there. */ if (!(forcetype != NULL && echo)) { int seq; if (device == NULL) { gpsd_report(LOG_ERROR, "device must be specified for low-level access.\n"); exit(1); } gpsd_init(&session, &context, device); gpsd_report(LOG_PROG, "initialization passed.\n"); if (gpsd_activate(&session) == -1) { gpsd_report(LOG_ERROR, "activation of device %s failed, errno=%d\n", device, errno); exit(2); } /* hunt for packet type and serial parameters */ for (seq = 0; session.device_type == NULL; seq++) { if (get_packet(&session) == ERROR_SET) { gpsd_report(LOG_ERROR, "autodetection failed.\n"); exit(2); } else { gpsd_report(LOG_IO, "autodetection after %d reads.\n", seq); (void) alarm(0); break; } } gpsd_report(LOG_PROG, "%s looks like a %s at %d.\n", device, gpsd_id(&session), session.gpsdata.dev.baudrate); if (forcetype!=NULL && strcmp("Generic NMEA", session.device_type->type_name) !=0 && strcmp(forcetype->type_name, session.device_type->type_name)!=0) { gpsd_report(LOG_ERROR, "'%s' doesn't match non-generic type '%s' of selected device.\n", forcetype->type_name, session.device_type->type_name); } /* * If we've identified this as an NMEA device, we have to eat * packets for a while to see if one of our probes elicits an * ID response telling us that it's really a SiRF or * something. If so, the libgpsd(3) layer will automatically * redispatch to the correct driver type. */ if (strcmp(session.device_type->type_name, "Generic NMEA") == 0) { int dummy; for (dummy = 0; dummy < REDIRECT_SNIFF; dummy++) { if ((get_packet(&session) & DEVICEID_SET)!=0) break; } } gpsd_report(LOG_SHOUT, "%s identified as a %s at %d.\n", device, gpsd_id(&session), session.gpsdata.dev.baudrate); } /* if no control operation was specified, we're done */ if (speed==NULL && !to_nmea && !to_binary && control==NULL) exit(0); /* maybe user wants to see the packet rather than send it */ if (echo) session.gpsdata.gps_fd = fileno(stdout); /* control op specified; maybe we forced the type */ if (forcetype != NULL) (void)gpsd_switch_driver(&session, forcetype->type_name); /* now perform the actual control function */ status = 0; #ifdef ALLOW_RECONFIGURE /*@ -nullderef @*/ if (to_nmea || to_binary) { if (session.device_type->mode_switcher == NULL) { gpsd_report(LOG_SHOUT, "%s devices have no mode switch.\n", session.device_type->type_name); status = 1; } else { int target_mode = to_nmea ? MODE_NMEA : MODE_BINARY; int target_type = to_nmea ? NMEA_PACKET : session.device_type->packet_type; gpsd_report(LOG_SHOUT, "switching to mode %s.\n", to_nmea ? "NMEA" : "BINARY"); session.device_type->mode_switcher(&session, target_mode); /* * Hunt for packet type again (mode might have * changed). We've found by experiment that you can't * close the connection to the device after a mode * change but before you see a packet of the right * type come back from it - otherwise you can hit a * timing window where the mode-change control message * gets ignored or flushed. */ if (!echo) { /* suppresses probing for subtypes */ context.readonly = true; (void)sleep(1); (void) alarm(timeout); for (;;) { if (get_packet(&session) == ERROR_SET) { continue; } else if (session.packet.type == target_type) { (void)alarm(0); break; } } context.readonly = false; } /*@ -nullpass @*/ gpsd_report(LOG_SHOUT, "after mode change, %s looks like a %s at %d.\n", device, gpsd_id(&session), session.gpsdata.dev.baudrate); /*@ +nullpass @*/ } } if (speed) { char parity = echo ? 'N': session.gpsdata.dev.parity; int stopbits = echo ? 1 : session.gpsdata.dev.stopbits; char *modespec; modespec = strchr(speed, ':'); /*@ +charint @*/ status = 0; if (modespec!=NULL) { *modespec = '\0'; if (strchr("78", *++modespec) == NULL) { gpsd_report(LOG_ERROR, "No support for that word lengths.\n"); status = 1; } parity = *++modespec; if (strchr("NOE", parity) == NULL) { gpsd_report(LOG_ERROR, "What parity is '%c'?\n", parity); status = 1; } stopbits = *++modespec; if (strchr("12", parity) == NULL) { gpsd_report(LOG_ERROR, "Stop bits must be 1 or 2.\n"); status = 1; } stopbits = (int)(stopbits-'0'); } if (status == 0) { if (session.device_type->speed_switcher == NULL) { gpsd_report(LOG_ERROR, "%s devices have no speed switch.\n", session.device_type->type_name); status = 1; } else if (session.device_type->speed_switcher(&session, (speed_t)atoi(speed), parity, stopbits)) { /* * See the 'deep black magic' comment in * gpsd.c:set_serial() Probably not needed here, * but it can't hurt. */ (void)tcdrain(session.gpsdata.gps_fd); (void)usleep(50000); gpsd_report(LOG_PROG, "%s change to %s%c%d succeeded\n", session.gpsdata.dev.path, speed, parity, stopbits); } else { gpsd_report(LOG_ERROR, "%s driver won't support %s%c%d.\n", session.gpsdata.dev.path, speed, parity, stopbits); status = 1; } } } if (rate) { bool write_enable = context.readonly; context.readonly = false; if (session.device_type->rate_switcher == NULL) { gpsd_report(LOG_ERROR, "%s devices have no rate switcher.\n", session.device_type->type_name); status = 1; } else { double rate_dbl = strtod(rate, NULL); if (!session.device_type->rate_switcher(&session, rate_dbl)) { gpsd_report(LOG_ERROR, "rate switch failed.\n"); status = 1; } } context.readonly = write_enable; } #endif /* ALLOW_RECONFIGURE */ #ifdef ALLOW_CONTROLSEND /*@ -compdef @*/ if (control) { bool write_enable = context.readonly; context.readonly = false; if (session.device_type->control_send == NULL) { gpsd_report(LOG_ERROR, "%s devices have no control sender.\n", session.device_type->type_name); status = 1; } else { if (session.device_type->control_send(&session, cooked, (size_t)cooklen) == -1) { gpsd_report(LOG_ERROR, "control transmission failed.\n"); status = 1; } } context.readonly = write_enable; } /*@ +compdef @*/ #endif /* ALLOW_CONTROLSEND */ if (forcetype == NULL || !echo) { /* * Give the device time to settle before closing it. Alas, this is * voodoo programming; we don't know it will have any effect, but * GPSes are notoriously prone to timing-dependent errors. */ (void)usleep(300000); gpsd_wrap(&session); } exit(status); /*@ +nullderef @*/ /*@ +mustfreeonly +immediatetrans @*/ } }
/** * Thread reading from gpsd. */ static void * cgps_thread (void * pData) { struct gps_data_t gpsd_conn; unsigned int err_count; cgps_thread_running = CGPS_TRUE; while (CGPS_TRUE) { pthread_mutex_lock (&cgps_thread_lock); if (cgps_thread_shutdown == CGPS_TRUE) { goto quit; } pthread_mutex_unlock (&cgps_thread_lock); err_count = 0; #if GPSD_API_MAJOR_VERSION > 4 int status = gps_open (cgps_config_data.host, cgps_config_data.port, &gpsd_conn); #else int status = gps_open_r (cgps_config_data.host, cgps_config_data.port, &gpsd_conn); #endif if (status < 0) { WARNING ("gps plugin: connecting to %s:%s failed: %s", cgps_config_data.host, cgps_config_data.port, gps_errstr (status)); // Here we make a pause until a new tentative to connect, we check also if // the thread does not need to stop. if (cgps_thread_pause(cgps_config_data.pause_connect) == CGPS_FALSE) { goto quit; } continue; } gps_stream (&gpsd_conn, WATCH_ENABLE | WATCH_JSON | WATCH_NEWSTYLE, NULL); gps_send (&gpsd_conn, CGPS_CONFIG); while (CGPS_TRUE) { pthread_mutex_lock (&cgps_thread_lock); if (cgps_thread_shutdown == CGPS_TRUE) { goto stop; } pthread_mutex_unlock (&cgps_thread_lock); #if GPSD_API_MAJOR_VERSION > 4 long timeout_us = CDTIME_T_TO_US (cgps_config_data.timeout); if (!gps_waiting (&gpsd_conn, (int) timeout_us )) #else if (!gps_waiting (&gpsd_conn)) #endif { continue; } if (gps_read (&gpsd_conn) == -1) { WARNING ("gps plugin: incorrect data! (err_count: %d)", err_count); err_count++; if (err_count > CGPS_MAX_ERROR) { // Server is not responding ... if (gps_send (&gpsd_conn, CGPS_CONFIG) == -1) { WARNING ("gps plugin: gpsd seems to be down, reconnecting"); gps_close (&gpsd_conn); break; } // Server is responding ... else { err_count = 0; } } continue; } pthread_mutex_lock (&cgps_data_lock); // Number of sats in view: cgps_data.sats_used = (gauge_t) gpsd_conn.satellites_used; cgps_data.sats_visible = (gauge_t) gpsd_conn.satellites_visible; // dilution of precision: cgps_data.vdop = NAN; cgps_data.hdop = NAN; if (cgps_data.sats_used > 0) { cgps_data.hdop = gpsd_conn.dop.hdop; cgps_data.vdop = gpsd_conn.dop.vdop; } DEBUG ("gps plugin: %.0f sats used (of %.0f visible), hdop = %.3f, vdop = %.3f", cgps_data.sats_used, cgps_data.sats_visible, cgps_data.hdop, cgps_data.vdop); pthread_mutex_unlock (&cgps_data_lock); } } stop: DEBUG ("gps plugin: thread closing gpsd connection ... "); gps_stream (&gpsd_conn, WATCH_DISABLE, NULL); gps_close (&gpsd_conn); quit: DEBUG ("gps plugin: thread shutting down ... "); cgps_thread_running = CGPS_FALSE; pthread_mutex_unlock (&cgps_thread_lock); pthread_exit (NULL); }