1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960 |
- /* libgpsd_core.c -- manage access to sensors
- *
- * Access to the driver layer goes through the entry points in this file.
- * The idea is to present a session as an abstraction from which you get
- * fixes (and possibly other data updates) by calling gpsd_multipoll(). The
- * rest is setup and teardown. (For backward compatibility the older gpsd_poll()
- * entry point has been retained.)
- *
- * This file is Copyright 2010 by the GPSD project
- * SPDX-License-Identifier: BSD-2-clause
- */
- #include "gpsd_config.h" /* must be before all includes */
- #include <assert.h>
- #include <ctype.h>
- #include <errno.h>
- #include <fcntl.h>
- #include <libgen.h>
- #include <math.h>
- #include <stdarg.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <syslog.h>
- #include <sys/select.h> // for pselect() per POSIX
- #include <sys/socket.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include <time.h>
- #include <unistd.h>
- #include "gpsd.h"
- #include "matrix.h"
- #include "strfuncs.h"
- #include "timespec.h"
- #if defined(NMEA2000_ENABLE)
- #include "driver_nmea2000.h"
- #endif /* defined(NMEA2000_ENABLE) */
- /* pass low-level data to devices straight through */
- ssize_t gpsd_write(struct gps_device_t *session,
- const char *buf,
- const size_t len)
- {
- return session->context->serial_write(session, buf, len);
- }
- static void basic_report(const char *buf)
- {
- (void)fputs(buf, stderr);
- }
- void errout_reset(struct gpsd_errout_t *errout)
- {
- errout->debug = LOG_SHOUT;
- errout->report = basic_report;
- }
- static pthread_mutex_t report_mutex;
- void gpsd_acquire_reporting_lock(void)
- {
- int err;
- err = pthread_mutex_lock(&report_mutex);
- if (0 != err) {
- /* POSIX says pthread_mutex_lock() should only fail if the
- thread holding the lock has died. Best for gpsd to just die
- because things are FUBAR. */
- (void)fprintf(stderr,"pthread_mutex_lock() failed: %s\n",
- strerror(err));
- exit(EXIT_FAILURE);
- }
- }
- void gpsd_release_reporting_lock(void)
- {
- int err;
- err = pthread_mutex_unlock(&report_mutex);
- if (0 != err) {
- /* POSIX says pthread_mutex_unlock() should only fail when
- trying to unlock a lock that does not exist, or is not owned by
- this thread. This should never happen, so best for gpsd to die
- because things are FUBAR. */
- (void)fprintf(stderr,"pthread_mutex_unlock() failed: %s\n",
- strerror(err));
- exit(EXIT_FAILURE);
- }
- }
- #ifndef SQUELCH_ENABLE
- static void visibilize(char *outbuf, size_t outlen,
- const char *inbuf, size_t inlen)
- {
- const char *sp;
- outbuf[0] = '\0';
- for (sp = inbuf; sp < inbuf + inlen && strlen(outbuf)+6 < outlen; sp++)
- if (isprint((unsigned char) *sp) || (sp[0] == '\n' && sp[1] == '\0')
- || (sp[0] == '\r' && sp[2] == '\0'))
- (void)snprintf(outbuf + strlen(outbuf), 2, "%c", *sp);
- else
- (void)snprintf(outbuf + strlen(outbuf), 6, "\\x%02x",
- 0x00ff & (unsigned)*sp);
- }
- #endif /* !SQUELCH_ENABLE */
- /* assemble msg in vprintf(3) style, use errout hook or syslog for delivery */
- static void gpsd_vlog(const int errlevel,
- const struct gpsd_errout_t *errout,
- char *outbuf, size_t outlen,
- const char *fmt, va_list ap)
- {
- #ifdef SQUELCH_ENABLE
- (void)errout;
- (void)errlevel;
- (void)fmt;
- #else
- char buf[BUFSIZ];
- char *err_str;
- // errout should never be NULL, but some code analyzers complain anyway
- if (NULL == errout ||
- errout->debug < errlevel) {
- return;
- }
- gpsd_acquire_reporting_lock();
- switch (errlevel) {
- case LOG_ERROR:
- err_str = "ERROR: ";
- break;
- case LOG_SHOUT:
- err_str = "SHOUT: ";
- break;
- case LOG_WARN:
- err_str = "WARN: ";
- break;
- case LOG_CLIENT:
- err_str = "CLIENT: ";
- break;
- case LOG_INF:
- err_str = "INFO: ";
- break;
- case LOG_DATA:
- err_str = "DATA: ";
- break;
- case LOG_PROG:
- err_str = "PROG: ";
- break;
- case LOG_IO:
- err_str = "IO: ";
- break;
- case LOG_SPIN:
- err_str = "SPIN: ";
- break;
- case LOG_RAW:
- err_str = "RAW: ";
- break;
- default:
- err_str = "UNK: ";
- }
- assert(errout->label != NULL);
- (void)strlcpy(buf, errout->label, sizeof(buf));
- (void)strlcat(buf, ":", sizeof(buf));
- (void)strlcat(buf, err_str, sizeof(buf));
- str_vappendf(buf, sizeof(buf), fmt, ap);
- visibilize(outbuf, outlen, buf, strlen(buf));
- if (getpid() == getsid(getpid()))
- syslog((errlevel <= LOG_SHOUT) ? LOG_ERR : LOG_NOTICE, "%s", outbuf);
- else if (errout->report != NULL)
- errout->report(outbuf);
- else
- (void)fputs(outbuf, stderr);
- gpsd_release_reporting_lock();
- #endif /* !SQUELCH_ENABLE */
- }
- /* assemble msg in printf(3) style, use errout hook or syslog for delivery */
- void gpsd_log(const int errlevel, const struct gpsd_errout_t *errout,
- const char *fmt, ...)
- {
- char buf[BUFSIZ];
- va_list ap;
- buf[0] = '\0';
- va_start(ap, fmt);
- gpsd_vlog(errlevel, errout, buf, sizeof(buf), fmt, ap);
- va_end(ap);
- }
- /* dump the current packet in a form optimised for eyeballs */
- const char *gpsd_prettydump(struct gps_device_t *session)
- {
- return gpsd_packetdump(session->msgbuf, sizeof(session->msgbuf),
- (char *)session->lexer.outbuffer,
- session->lexer.outbuflen);
- }
- /* Define the possible hook strings here so we can get the length */
- #define HOOK_ACTIVATE "ACTIVATE"
- #define HOOK_DEACTIVATE "DEACTIVATE"
- #define HOOK_CMD_MAX (sizeof(DEVICEHOOKPATH) + GPS_PATH_MAX \
- + sizeof(HOOK_DEACTIVATE))
- static void gpsd_run_device_hook(struct gpsd_errout_t *errout,
- char *device_name, char *hook)
- {
- struct stat statbuf;
- if (stat(DEVICEHOOKPATH, &statbuf) == -1)
- GPSD_LOG(LOG_PROG, errout,
- "no %s present, skipped running %s hook\n",
- DEVICEHOOKPATH, hook);
- else {
- int status;
- char buf[HOOK_CMD_MAX];
- (void)snprintf(buf, sizeof(buf), "%s %s %s",
- DEVICEHOOKPATH, device_name, hook);
- GPSD_LOG(LOG_INF, errout, "running %s\n", buf);
- status = system(buf);
- if (status == -1)
- GPSD_LOG(LOG_ERROR, errout, "error running %s\n", buf);
- else
- GPSD_LOG(LOG_INF, errout,
- "%s returned %d\n", DEVICEHOOKPATH,
- WEXITSTATUS(status));
- }
- }
- int gpsd_switch_driver(struct gps_device_t *session, char *type_name)
- {
- const struct gps_type_t **dp;
- bool first_sync = (session->device_type != NULL);
- unsigned int i;
- if (first_sync && strcmp(session->device_type->type_name, type_name) == 0)
- return 0;
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "switch_driver(%s) called...\n", type_name);
- for (dp = gpsd_drivers, i = 0; *dp; dp++, i++)
- if (strcmp((*dp)->type_name, type_name) == 0) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "selecting %s driver...\n",
- (*dp)->type_name);
- gpsd_assert_sync(session);
- session->device_type = *dp;
- session->driver_index = i;
- session->gpsdata.dev.mincycle = session->device_type->min_cycle;
- /* reconfiguration might be required */
- if (first_sync && session->device_type->event_hook != NULL)
- session->device_type->event_hook(session,
- event_driver_switch);
- if (STICKY(*dp))
- session->last_controller = *dp;
- return 1;
- }
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "invalid GPS type \"%s\".\n", type_name);
- return 0;
- }
- void gps_context_init(struct gps_context_t *context,
- const char *label)
- {
- (void)memset(context, '\0', sizeof(struct gps_context_t));
- //context.readonly = false;
- context->leap_notify = LEAP_NOWARNING;
- context->serial_write = gpsd_serial_write;
- errout_reset(&context->errout);
- context->errout.label = (char *)label;
- (void)pthread_mutex_init(&report_mutex, NULL);
- }
- /* initialize GPS polling */
- void gpsd_init(struct gps_device_t *session, struct gps_context_t *context,
- const char *device)
- {
- if (device != NULL)
- (void)strlcpy(session->gpsdata.dev.path, device,
- sizeof(session->gpsdata.dev.path));
- session->device_type = NULL; /* start by hunting packets */
- session->last_controller = NULL;
- session->observed = 0;
- session->sourcetype = source_unknown; /* gpsd_open() sets this */
- session->servicetype = service_unknown; /* gpsd_open() sets this */
- session->context = context;
- memset(session->subtype, 0, sizeof(session->subtype));
- memset(session->subtype1, 0, sizeof(session->subtype1));
- #ifdef NMEA0183_ENABLE
- memset(&(session->nmea), 0, sizeof(session->nmea));
- #endif /* NMEA0183_ENABLE */
- gps_clear_fix(&session->gpsdata.fix);
- gps_clear_fix(&session->newdata);
- gps_clear_fix(&session->lastfix);
- gps_clear_fix(&session->oldfix);
- session->gpsdata.set = 0;
- gps_clear_att(&session->gpsdata.attitude);
- gps_clear_dop(&session->gpsdata.dop);
- gps_clear_log(&session->gpsdata.log);
- session->gpsdata.dev.mincycle.tv_sec = 1;
- session->gpsdata.dev.mincycle.tv_nsec = 0;
- session->gpsdata.dev.cycle.tv_sec = 1;
- session->gpsdata.dev.cycle.tv_nsec = 0;
- session->sor.tv_sec = 0;
- session->sor.tv_nsec = 0;
- session->chars = 0;
- /* tty-level initialization */
- gpsd_tty_init(session);
- /* necessary in case we start reading in the middle of a GPGSV sequence */
- gpsd_zero_satellites(&session->gpsdata);
- /* initialize things for the packet parser */
- packet_reset(&session->lexer);
- }
- /* temporarily release the GPS device */
- void gpsd_deactivate(struct gps_device_t *session)
- {
- if (!session->context->readonly
- && session->device_type != NULL
- && session->device_type->event_hook != NULL) {
- session->device_type->event_hook(session, event_deactivate);
- }
- GPSD_LOG(LOG_INF, &session->context->errout,
- "closing GPS=%s (%d)\n",
- session->gpsdata.dev.path, session->gpsdata.gps_fd);
- #if defined(NMEA2000_ENABLE)
- if (session->sourcetype == source_can)
- (void)nmea2000_close(session);
- else
- #endif /* of defined(NMEA2000_ENABLE) */
- (void)gpsd_close(session);
- if (session->mode == O_OPTIMIZE)
- gpsd_run_device_hook(&session->context->errout,
- session->gpsdata.dev.path,
- HOOK_DEACTIVATE);
- /* tell any PPS-watcher thread to die */
- session->pps_thread.report_hook = NULL;
- /* mark it inactivated */
- session->gpsdata.online.tv_sec = 0;
- session->gpsdata.online.tv_nsec = 0;
- }
- /* shim function to decouple PPS monitor code from the session structure */
- static void ppsthread_log(volatile struct pps_thread_t *pps_thread,
- int loglevel, const char *fmt, ...)
- {
- struct gps_device_t *device = (struct gps_device_t *)pps_thread->context;
- char buf[BUFSIZ];
- va_list ap;
- switch (loglevel) {
- case THREAD_ERROR:
- loglevel = LOG_ERROR;
- break;
- case THREAD_WARN:
- loglevel = LOG_WARN;
- break;
- case THREAD_INF:
- loglevel = LOG_INF;
- break;
- case THREAD_PROG:
- loglevel = LOG_PROG;
- break;
- case THREAD_RAW:
- loglevel = LOG_RAW;
- break;
- }
- buf[0] = '\0';
- va_start(ap, fmt);
- gpsd_vlog(loglevel, &device->context->errout, buf, sizeof(buf), fmt, ap);
- va_end(ap);
- }
- /* device has been opened - clear its storage for use */
- void gpsd_clear(struct gps_device_t *session)
- {
- (void)clock_gettime(CLOCK_REALTIME, &session->gpsdata.online);
- lexer_init(&session->lexer);
- session->lexer.errout = session->context->errout;
- // session->gpsdata.online = 0;
- gps_clear_att(&session->gpsdata.attitude);
- gps_clear_dop(&session->gpsdata.dop);
- gps_clear_fix(&session->gpsdata.fix);
- session->releasetime = (time_t)0;
- session->badcount = 0;
- /* clear the private data union */
- memset( (void *)&session->driver, '\0', sizeof(session->driver));
- /* set up the context structure for the PPS thread monitor */
- memset((void *)&session->pps_thread, 0, sizeof(session->pps_thread));
- session->pps_thread.devicefd = session->gpsdata.gps_fd;
- session->pps_thread.devicename = session->gpsdata.dev.path;
- session->pps_thread.log_hook = ppsthread_log;
- session->pps_thread.context = (void *)session;
- session->opentime = time(NULL);
- }
- /* split s into host and service parts
- * if service is not specified, *service is assigned to NULL
- * device is currently always assigned to NULL
- * return: -1 on error, 0 otherwise
- */
- int parse_uri_dest(char *s, char **host, char **service, char **device)
- {
- char *search = s;
- if (s[0] == '[') {
- /* IPv6 literal */
- char *cb = strchr(s, ']');
- if (!cb) {
- // missing terminating ]
- return -1;
- }
- *cb = '\0';
- *host = s + 1;
- search = cb + 1;
- } else {
- // IPv4 literal, or hostname
- *host = s;
- }
- s = strchr(search, ':');
- if (s) {
- // found a colon, remove it from host
- *s = '\0';
- search = s + 1;
- if (s[1] && ':' != s[1]) {
- // s[1] start port/service
- *service = s + 1;
- } else {
- *service = NULL;
- }
- } else {
- *service = NULL;
- }
- s = strchr(search, ':');
- if (s) {
- // found a colon, remove it
- *s = '\0';
- if (s[1]) {
- *device = s + 1;
- } else {
- *device = NULL;
- }
- } else {
- *device = NULL;
- }
- return 0;
- }
- /* open a device for access to its data *
- * return: the opened file descriptor
- * PLACEHOLDING_FD - for /dev/ppsX
- * UNALLOCATED_FD - for open failure
- * -1 - for open failure
- */
- int gpsd_open(struct gps_device_t *session)
- {
- #ifdef NETFEED_ENABLE
- /* special case: source may be a URI to a remote GNSS or DGPS service */
- if (netgnss_uri_check(session->gpsdata.dev.path)) {
- session->gpsdata.gps_fd = netgnss_uri_open(session,
- session->gpsdata.dev.path);
- session->sourcetype = source_tcp;
- GPSD_LOG(LOG_SPIN, &session->context->errout,
- "netgnss_uri_open(%s) returns socket on fd %d\n",
- session->gpsdata.dev.path, session->gpsdata.gps_fd);
- return session->gpsdata.gps_fd;
- /* otherwise, could be an TCP data feed */
- } else if (str_starts_with(session->gpsdata.dev.path, "tcp://")) {
- char server[GPS_PATH_MAX], *host, *port, *device;
- socket_t dsock;
- (void)strlcpy(server, session->gpsdata.dev.path + 6, sizeof(server));
- INVALIDATE_SOCKET(session->gpsdata.gps_fd);
- if (-1 == parse_uri_dest(server, &host, &port, &device) ||
- !port) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "Missing service in TCP feed spec.\n");
- return -1;
- }
- GPSD_LOG(LOG_INF, &session->context->errout,
- "opening TCP feed at %s, port %s.\n", host,
- port);
- if ((dsock = netlib_connectsock(AF_UNSPEC, host, port, "tcp")) < 0) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "TCP device open error %s.\n",
- netlib_errstr(dsock));
- return -1;
- } else
- GPSD_LOG(LOG_SPIN, &session->context->errout,
- "TCP device opened on fd %d\n", dsock);
- session->gpsdata.gps_fd = dsock;
- session->sourcetype = source_tcp;
- return session->gpsdata.gps_fd;
- /* or could be UDP */
- } else if (str_starts_with(session->gpsdata.dev.path, "udp://")) {
- char server[GPS_PATH_MAX], *host, *port, *device;
- socket_t dsock;
- (void)strlcpy(server, session->gpsdata.dev.path + 6, sizeof(server));
- INVALIDATE_SOCKET(session->gpsdata.gps_fd);
- if (-1 == parse_uri_dest(server, &host, &port, &device) ||
- !port) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "Missing service in UDP feed spec.\n");
- return -1;
- }
- GPSD_LOG(LOG_INF, &session->context->errout,
- "opening UDP feed at %s, port %s.\n", host,
- port);
- if ((dsock = netlib_connectsock(AF_UNSPEC, host, port, "udp")) < 0) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "UDP device open error %s.\n",
- netlib_errstr(dsock));
- return -1;
- } else
- GPSD_LOG(LOG_SPIN, &session->context->errout,
- "UDP device opened on fd %d\n", dsock);
- session->gpsdata.gps_fd = dsock;
- session->sourcetype = source_udp;
- return session->gpsdata.gps_fd;
- }
- #endif /* NETFEED_ENABLE */
- #ifdef PASSTHROUGH_ENABLE
- if (str_starts_with(session->gpsdata.dev.path, "gpsd://")) {
- /* could be:
- * gpsd://[ipv6]
- * gpsd://ipv4
- * gpsd://hostname
- * gpsd://[ipv6]:port
- * gpsd://ipv4:port
- * gpsd://hostname:port
- * gpsd://[ipv6]:port:/device
- * gpsd://ipv4:port:/device
- * gpsd://hostname:port:/device
- * gpsd://[ipv6]::/device
- * gpsd://ipv4::/device
- * gpsd://hostname::/device
- */
- char server[GPS_PATH_MAX], *host, *port, *device;
- socket_t dsock;
- (void)strlcpy(server, session->gpsdata.dev.path + 7, sizeof(server));
- INVALIDATE_SOCKET(session->gpsdata.gps_fd);
- if (-1 == parse_uri_dest(server, &host, &port, &device)) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "Malformed URI specified.\n");
- return -1;
- }
- if (!port)
- port = DEFAULT_GPSD_PORT;
- GPSD_LOG(LOG_INF, &session->context->errout,
- "opening remote gpsd feed at %s, port %s.\n",
- host, port);
- if ((dsock = netlib_connectsock(AF_UNSPEC, host, port, "tcp")) < 0) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "remote gpsd device open error %s.\n",
- netlib_errstr(dsock));
- return -1;
- } else
- GPSD_LOG(LOG_SPIN, &session->context->errout,
- "remote gpsd feed opened on fd %d\n", dsock);
- /* watch to remote is issued when WATCH is */
- session->gpsdata.gps_fd = dsock;
- session->sourcetype = source_gpsd;
- return session->gpsdata.gps_fd;
- }
- #endif /* PASSTHROUGH_ENABLE */
- #if defined(NMEA2000_ENABLE)
- if (str_starts_with(session->gpsdata.dev.path, "nmea2000://")) {
- return nmea2000_open(session);
- }
- #endif /* defined(NMEA2000_ENABLE) */
- /* fall through to plain serial open */
- /* could be a naked /dev/ppsX */
- return gpsd_serial_open(session);
- }
- int gpsd_activate(struct gps_device_t *session, const int mode)
- /* acquire a connection to the GPS device */
- {
- if (mode == O_OPTIMIZE)
- gpsd_run_device_hook(&session->context->errout,
- session->gpsdata.dev.path, HOOK_ACTIVATE);
- session->gpsdata.gps_fd = gpsd_open(session);
- if (mode != O_CONTINUE)
- session->mode = mode;
- // cppcheck-suppress pointerLessThanZero
- if (session->gpsdata.gps_fd < 0) {
- /* return could be -1, PLACEHOLDING_FD, of UNALLOCATED_FD */
- if ( PLACEHOLDING_FD == session->gpsdata.gps_fd ) {
- /* it is /dev/ppsX, need to set devicename, etc. */
- gpsd_clear(session);
- }
- return session->gpsdata.gps_fd;
- }
- #ifdef NON_NMEA0183_ENABLE
- /* if it's a sensor, it must be probed */
- if ((session->servicetype == service_sensor) &&
- (session->sourcetype != source_can)) {
- const struct gps_type_t **dp;
- for (dp = gpsd_drivers; *dp; dp++) {
- if ((*dp)->probe_detect != NULL) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "Probing \"%s\" driver...\n",
- (*dp)->type_name);
- /* toss stale data */
- (void)tcflush(session->gpsdata.gps_fd, TCIOFLUSH);
- if ((*dp)->probe_detect(session) != 0) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "Probe found \"%s\" driver...\n",
- (*dp)->type_name);
- session->device_type = *dp;
- gpsd_assert_sync(session);
- goto foundit;
- } else
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "Probe not found \"%s\" driver...\n",
- (*dp)->type_name);
- }
- }
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "no probe matched...\n");
- }
- foundit:
- #endif /* NON_NMEA0183_ENABLE */
- gpsd_clear(session);
- GPSD_LOG(LOG_INF, &session->context->errout,
- "gpsd_activate(%d): activated GPS (fd %d)\n",
- session->mode, session->gpsdata.gps_fd);
- /*
- * We might know the device's type, but we shouldn't assume it has
- * retained its settings. A revert hook might well have undone
- * them on the previous close. Fire a reactivate event so drivers
- * can do something about this if they choose.
- */
- if (session->device_type != NULL
- && session->device_type->event_hook != NULL)
- session->device_type->event_hook(session, event_reactivate);
- return session->gpsdata.gps_fd;
- }
- /*****************************************************************************
- Carl Carter of SiRF supplied this algorithm for computing DOPs from
- a list of visible satellites (some typos corrected)...
- For satellite n, let az(n) = azimuth angle from North and el(n) be elevation.
- Let:
- a(k, 1) = sin az(k) * cos el(k)
- a(k, 2) = cos az(k) * cos el(k)
- a(k, 3) = sin el(k)
- Then form the line-of-sight matrix A for satellites used in the solution:
- | a(1,1) a(1,2) a(1,3) 1 |
- | a(2,1) a(2,2) a(2,3) 1 |
- | : : : : |
- | a(n,1) a(n,2) a(n,3) 1 |
- And its transpose A~:
- |a(1, 1) a(2, 1) . . . a(n, 1) |
- |a(1, 2) a(2, 2) . . . a(n, 2) |
- |a(1, 3) a(2, 3) . . . a(n, 3) |
- | 1 1 . . . 1 |
- Compute the covariance matrix (A~*A)^-1, which is guaranteed symmetric:
- | s(x)^2 s(x)*s(y) s(x)*s(z) s(x)*s(t) |
- | s(y)*s(x) s(y)^2 s(y)*s(z) s(y)*s(t) |
- | s(z)*s(x) s(z)*s(y) s(z)^2 s(z)*s(t) |
- | s(t)*s(x) s(t)*s(y) s(t)*s(z) s(t)^2 |
- Then:
- GDOP = sqrt(s(x)^2 + s(y)^2 + s(z)^2 + s(t)^2)
- TDOP = sqrt(s(t)^2)
- PDOP = sqrt(s(x)^2 + s(y)^2 + s(z)^2)
- HDOP = sqrt(s(x)^2 + s(y)^2)
- VDOP = sqrt(s(z)^2)
- Here's how we implement it...
- First, each compute element P(i,j) of the 4x4 product A~*A.
- If S(k=1,k=n): f(...) is the sum of f(...) as k varies from 1 to n, then
- applying the definition of matrix product tells us:
- P(i,j) = S(k=1,k=n): B(i, k) * A(k, j)
- But because B is the transpose of A, this reduces to
- P(i,j) = S(k=1,k=n): A(k, i) * A(k, j)
- This is not, however, the entire algorithm that SiRF uses. Carl writes:
- > As you note, with rounding accounted for, most values agree exactly, and
- > those that don't agree our number is higher. That is because we
- > deweight some satellites and account for that in the DOP calculation.
- > If a satellite is not used in a solution at the same weight as others,
- > it should not contribute to DOP calculation at the same weight. So our
- > internal algorithm does a compensation for that which you would have no
- > way to duplicate on the outside since we don't output the weighting
- > factors. In fact those are not even available to API users.
- Queried about the deweighting, Carl says:
- > In the SiRF tracking engine, each satellite track is assigned a quality
- > value based on the tracker's estimate of that signal. It includes C/No
- > estimate, ability to hold onto the phase, stability of the I vs. Q phase
- > angle, etc. The navigation algorithm then ranks all the tracks into
- > quality order and selects which ones to use in the solution and what
- > weight to give those used in the solution. The process is actually a
- > bit of a "trial and error" method -- we initially use all available
- > tracks in the solution, then we sequentially remove the lowest quality
- > ones until the solution stabilizes. The weighting is inherent in the
- > Kalman filter algorithm. Once the solution is stable, the DOP is
- > computed from those SVs used, and there is an algorithm that looks at
- > the quality ratings and determines if we need to deweight any.
- > Likewise, if we use altitude hold mode for a 3-SV solution, we deweight
- > the phantom satellite at the center of the Earth.
- So we cannot exactly duplicate what SiRF does internally. We'll leave
- HDOP alone and use our computed values for VDOP and PDOP. Note, this
- may have to change in the future if this code is used by a non-SiRF
- driver.
- ******************************************************************************/
- static gps_mask_t fill_dop(const struct gpsd_errout_t *errout,
- const struct gps_data_t * gpsdata,
- struct dop_t * dop)
- {
- double prod[4][4];
- double inv[4][4];
- double satpos[MAXCHANNELS][4];
- double xdop, ydop, hdop, vdop, pdop, tdop, gdop;
- int i, j, k, n;
- memset(satpos, 0, sizeof(satpos));
- for (n = k = 0; k < gpsdata->satellites_visible; k++) {
- if (!gpsdata->skyview[k].used) {
- /* skip unused sats */
- continue;
- }
- if (1 > gpsdata->skyview[k].PRN) {
- /* skip bad PRN */
- continue;
- }
- if (0 == isfinite(gpsdata->skyview[k].azimuth) ||
- 0 > gpsdata->skyview[k].azimuth ||
- 359 < gpsdata->skyview[k].azimuth) {
- /* skip bad azimuth */
- continue;
- }
- if (0 == isfinite(gpsdata->skyview[k].elevation) ||
- 90 < fabs(gpsdata->skyview[k].elevation)) {
- /* skip bad elevation */
- continue;
- }
- const struct satellite_t *sp = &gpsdata->skyview[k];
- satpos[n][0] = sin(sp->azimuth * DEG_2_RAD)
- * cos(sp->elevation * DEG_2_RAD);
- satpos[n][1] = cos(sp->azimuth * DEG_2_RAD)
- * cos(sp->elevation * DEG_2_RAD);
- satpos[n][2] = sin(sp->elevation * DEG_2_RAD);
- satpos[n][3] = 1;
- GPSD_LOG(LOG_INF, errout, "PRN=%3d az=%.1f ael%.1f (%f, %f, %f)\n",
- gpsdata->skyview[k].PRN,
- gpsdata->skyview[k].azimuth,
- gpsdata->skyview[k].elevation,
- satpos[n][0], satpos[n][1], satpos[n][2]);
- n++;
- }
- /* can't use gpsdata->satellites_used as that is a counter for xxGSA,
- * and gets cleared at odd times */
- GPSD_LOG(LOG_INF, errout, "Sats used (%d):\n", n);
- /* If we don't have 4 satellites then we don't have enough
- * information to calculate DOPS */
- if (n < 4) {
- #ifdef __UNUSED__
- GPSD_LOG(LOG_RAW, errout,
- "Not enough satellites available %d < 4:\n",
- n);
- #endif /* __UNUSED__ */
- /* Is this correct return code here? or should it be ERROR_SET */
- return 0;
- }
- memset(prod, 0, sizeof(prod));
- memset(inv, 0, sizeof(inv));
- #ifdef __UNUSED__
- GPSD_LOG(LOG_INF, errout, "Line-of-sight matrix:\n");
- for (k = 0; k < n; k++) {
- GPSD_LOG(LOG_INF, errout, "%f %f %f %f\n",
- satpos[k][0], satpos[k][1], satpos[k][2], satpos[k][3]);
- }
- #endif /* __UNUSED__ */
- for (i = 0; i < 4; ++i) { //< rows
- for (j = 0; j < 4; ++j) { //< cols
- prod[i][j] = 0.0;
- for (k = 0; k < n; ++k) {
- prod[i][j] += satpos[k][i] * satpos[k][j];
- }
- }
- }
- #ifdef __UNUSED__
- GPSD_LOG(LOG_INF, errout, "product:\n");
- for (k = 0; k < 4; k++) {
- GPSD_LOG(LOG_INF, errout, "%f %f %f %f\n",
- prod[k][0], prod[k][1], prod[k][2], prod[k][3]);
- }
- #endif /* __UNUSED__ */
- if (matrix_invert(prod, inv)) {
- #ifdef __UNUSED__
- /*
- * Note: this will print garbage unless all the subdeterminants
- * are computed in the invert() function.
- */
- GPSD_LOG(LOG_RAW, errout, "inverse:\n");
- for (k = 0; k < 4; k++) {
- GPSD_LOG(LOG_RAW, errout,
- "%f %f %f %f\n",
- inv[k][0], inv[k][1], inv[k][2], inv[k][3]);
- }
- #endif /* __UNUSED__ */
- } else {
- GPSD_LOG(LOG_DATA, errout,
- "LOS matrix is singular, can't calculate DOPs - source '%s'\n",
- gpsdata->dev.path);
- return 0;
- }
- xdop = sqrt(inv[0][0]);
- ydop = sqrt(inv[1][1]);
- hdop = sqrt(inv[0][0] + inv[1][1]);
- vdop = sqrt(inv[2][2]);
- pdop = sqrt(inv[0][0] + inv[1][1] + inv[2][2]);
- tdop = sqrt(inv[3][3]);
- gdop = sqrt(inv[0][0] + inv[1][1] + inv[2][2] + inv[3][3]);
- GPSD_LOG(LOG_DATA, errout,
- "DOPS computed/reported: X=%f/%f, Y=%f/%f, H=%f/%f, V=%f/%f, "
- "P=%f/%f, T=%f/%f, G=%f/%f\n",
- xdop, dop->xdop, ydop, dop->ydop, hdop, dop->hdop, vdop,
- dop->vdop, pdop, dop->pdop, tdop, dop->tdop, gdop, dop->gdop);
- /* Check to see which DOPs we already have. Save values if no value
- * from the GPS. Do not overwrite values which came from the GPS */
- if (isfinite(dop->xdop) == 0) {
- dop->xdop = xdop;
- }
- if (isfinite(dop->ydop) == 0) {
- dop->ydop = ydop;
- }
- if (isfinite(dop->hdop) == 0) {
- dop->hdop = hdop;
- }
- if (isfinite(dop->vdop) == 0) {
- dop->vdop = vdop;
- }
- if (isfinite(dop->pdop) == 0) {
- dop->pdop = pdop;
- }
- if (isfinite(dop->tdop) == 0) {
- dop->tdop = tdop;
- }
- if (isfinite(dop->gdop) == 0) {
- dop->gdop = gdop;
- }
- return DOP_SET;
- }
- /* compute errors and derived quantities
- * also a handy place to do final sanity checking */
- static void gpsd_error_model(struct gps_device_t *session)
- {
- struct gps_fix_t *fix; /* current fix */
- struct gps_fix_t *lastfix; /* last fix, maybe same time stamp */
- struct gps_fix_t *oldfix; /* old fix, previous time stamp */
- struct gps_fix_t *newfix; /* new fix (just merged) */
- double deltatime = -1.0; /* time span to compute rates */
- /*
- * Now we compute derived quantities. This is where the tricky error-
- * modeling stuff goes. Presently we don't know how to derive
- * time error.
- *
- * Some drivers set the position-error fields. Only the Zodiacs
- * report speed error. No NMEA 183 reports climb error. GPXTE
- * and PSRFEPE can report track error, but are rare.
- *
- * The UERE constants are our assumption about the base error of
- * GPS fixes in different directions.
- */
- #define H_UERE_NO_DGPS 15.0 /* meters, 95% confidence */
- #define H_UERE_WITH_DGPS 3.75 /* meters, 95% confidence */
- #define V_UERE_NO_DGPS 23.0 /* meters, 95% confidence */
- #define V_UERE_WITH_DGPS 5.75 /* meters, 95% confidence */
- #define P_UERE_NO_DGPS 19.0 /* meters, 95% confidence */
- #define P_UERE_WITH_DGPS 4.75 /* meters, 95% confidence */
- double h_uere, v_uere, p_uere;
- if (NULL == session)
- return;
- fix = &session->gpsdata.fix;
- lastfix = &session->lastfix;
- oldfix = &session->oldfix;
- newfix = &session->newdata; /* For whether rcvr supplies values */
- if (0 < fix->time.tv_sec) {
- /* we have a time for this merge data */
- deltatime = TS_SUB_D(&fix->time, &lastfix->time);
- if (0.0099 < fabs(deltatime)) {
- /* Time just moved, probably forward at least 10 ms.
- * Lastfix is now the previous (old) fix. */
- *oldfix = *lastfix;
- } else {
- // compute delta from old fix
- deltatime = TS_SUB_D(&fix->time, &oldfix->time);
- }
- }
- /* Sanity check for negative delta? */
- // adjusting UERE for DGPS is dodgy...
- h_uere =
- (session->gpsdata.fix.status ==
- STATUS_DGPS_FIX ? H_UERE_WITH_DGPS : H_UERE_NO_DGPS);
- v_uere =
- (session->gpsdata.fix.status ==
- STATUS_DGPS_FIX ? V_UERE_WITH_DGPS : V_UERE_NO_DGPS);
- p_uere =
- (session->gpsdata.fix.status ==
- STATUS_DGPS_FIX ? P_UERE_WITH_DGPS : P_UERE_NO_DGPS);
- if (0 == isfinite(fix->latitude) ||
- 0 == isfinite(fix->longitude) || /* both lat/lon, or none */
- 90.0 < fabs(fix->latitude) || /* lat out of range */
- 180.0 < fabs(fix->longitude)) { /* lon out of range */
- fix->latitude = fix->longitude = NAN;
- }
- /* validate ECEF */
- if (0 == isfinite(fix->ecef.x) ||
- 0 == isfinite(fix->ecef.y) ||
- 0 == isfinite(fix->ecef.z) ||
- 10.0 >= (fabs(fix->ecef.x) +
- fabs(fix->ecef.y) +
- fabs(fix->ecef.z))) { /* all zeros */
- fix->ecef.x = fix->ecef.y = fix->ecef.z = NAN;
- }
- /* if we have not lat/lon, but do have ECEF, calculate lat/lon */
- if ((0 == isfinite(fix->longitude) ||
- 0 == isfinite(fix->latitude)) &&
- 0 != isfinite(fix->ecef.x)) {
- session->gpsdata.set |= ecef_to_wgs84fix(fix,
- fix->ecef.x, fix->ecef.y,
- fix->ecef.z, fix->ecef.vx,
- fix->ecef.vy, fix->ecef.vz);
- }
- /* If you are in a rocket, and your GPS is ITAR unlocked, then
- * triple check these sanity checks.
- *
- * u-blox 8: Max altitude: 50,000m
- * Max horizontal speed: 250 m/s
- * Max climb: 100 m/s
- *
- * u-blox ZED-F9P: Max Velocity: 500 m/s
- */
- /* sanity check the speed, 10,000 m/s should be a nice max
- * Low Earth Orbit (LEO) is about 7,800 m/s */
- if (9999.9 < fabs(fix->speed))
- fix->speed = NAN;
- if (9999.9 < fabs(fix->NED.velN))
- fix->NED.velN = NAN;
- if (9999.9 < fabs(fix->NED.velE))
- fix->NED.velE = NAN;
- if (9999.9 < fabs(fix->NED.velD))
- fix->NED.velD = NAN;
- /* sanity check the climb, 10,000 m/s should be a nice max */
- if (9999.9 < fabs(fix->climb))
- fix->climb = NAN;
- if (0 != isfinite(fix->NED.velD) &&
- 0 == isfinite(fix->climb)) {
- /* have good velD, use it for climb */
- fix->climb = -fix->NED.velD;
- }
- /* compute speed and track from velN and velE if needed and possible */
- if (0 != isfinite(fix->NED.velN) &&
- 0 != isfinite(fix->NED.velE)) {
- if (0 == isfinite(fix->speed)) {
- fix->speed = hypot(fix->NED.velN, fix->NED.velE);
- }
- if (0 == isfinite(fix->track)) {
- fix->track = atan2(fix->NED.velE, fix->NED.velN) * RAD_2_DEG;
- // normalized later
- }
- }
- /*
- * OK, this is not an error computation, but we're at the right
- * place in the architecture for it. Compute geoid separation
- * and altHAE and altMSL in the simplest possible way.
- */
- /* geoid (ellipsoid) separation and variation */
- if (0 != isfinite(fix->latitude) &&
- 0 != isfinite(fix->longitude)) {
- if (0 == isfinite(fix->geoid_sep)) {
- fix->geoid_sep = wgs84_separation(fix->latitude,
- fix->longitude);
- }
- if (0 == isfinite(fix->magnetic_var) ||
- 0.09 >= fabs(fix->magnetic_var)) {
- /* some GPS set 0.0,E, or 0,W instead of blank */
- fix->magnetic_var = mag_var(fix->latitude,
- fix->longitude);
- }
- }
- if (0 != isfinite(fix->magnetic_var)) {
- if (0 == isfinite(fix->magnetic_track) &&
- 0 != isfinite(fix->track)) {
- // calculate mag track, normalized later
- fix->magnetic_track = fix->track + fix->magnetic_var;
- } else if (0 != isfinite(fix->magnetic_track) &&
- 0 == isfinite(fix->track)) {
- // calculate true track, normalized later
- fix->track = fix->magnetic_track - fix->magnetic_var;
- }
- }
- if (0 != isfinite(fix->track)) {
- // normalize true track
- DEG_NORM(fix->track);
- }
- if (0 != isfinite(fix->magnetic_track)) {
- // normalize mag track
- DEG_NORM(fix->magnetic_track);
- }
- if (0 != isfinite(fix->geoid_sep)) {
- if (0 != isfinite(fix->altHAE) &&
- 0 == isfinite(fix->altMSL)) {
- /* compute missing altMSL */
- fix->altMSL = fix->altHAE - fix->geoid_sep;
- } else if (0 == isfinite(fix->altHAE) &&
- 0 != isfinite(fix->altMSL)) {
- /* compute missing altHAE */
- fix->altHAE = fix->altMSL + fix->geoid_sep;
- }
- }
- /*
- * OK, this is not an error computation, but we're at the right
- * place in the architecture for it. Compute speed over ground
- * and climb/sink in the simplest possible way.
- */
- #ifdef __UNUSED__
- // debug code
- {
- char tbuf[JSON_DATE_MAX+1];
- GPSD_LOG(LOG_SHOUT, &session->context->errout,
- "time %s deltatime %f\n",
- timespec_to_iso8601(fix->time, tbuf, sizeof(tbuf)),
- deltatime);
- }
- #endif // __UNUSED__
- if (0 < deltatime) {
- /* have a valid time duration */
- /* FIXME! ignore if large. maybe > 1 hour? */
- if (MODE_2D <= fix->mode &&
- MODE_2D <= oldfix->mode) {
- if (0 == isfinite(fix->speed)) {
- fix->speed = earth_distance(fix->latitude,
- fix->longitude,
- oldfix->latitude,
- oldfix->longitude) / deltatime;
- /* sanity check */
- if (9999.9 < fabs(fix->speed))
- fix->speed = NAN;
- }
- if (MODE_3D <= fix->mode &&
- MODE_3D <= oldfix->mode &&
- 0 == isfinite(fix->climb) &&
- 0 != isfinite(fix->altHAE) &&
- 0 != isfinite(oldfix->altHAE)) {
- fix->climb = (fix->altHAE - oldfix->altHAE) / deltatime;
- /* sanity check the climb */
- if (9999.9 < fabs(fix->climb))
- fix->climb = NAN;
- }
- }
- }
- /*
- * Field reports match the theoretical prediction that
- * expected time error should be half the resolution of
- * the GPS clock, so we put the bound of the error
- * in as a constant pending getting it from each driver.
- *
- * In an ideal world, we'd increase this if no leap-second has
- * been seen and it's less than 750s (one almanac load cycle) from
- * device powerup. Alas, we have no way to know when device
- * powerup occurred - depending on the receiver design it could be
- * when the hardware was first powered up or when it was first
- * opened. Also, some devices (notably plain NMEA0183 receivers)
- * never ship an indication of when they have valid leap second.
- */
- if (0 < fix->time.tv_sec &&
- 0 == isfinite(fix->ept)) {
- /* can we compute ept from tdop? */
- fix->ept = 0.005;
- }
- /* Other error computations depend on having a valid fix */
- if (MODE_2D <= fix->mode) {
- if (0 == isfinite(newfix->epx) &&
- 0 != isfinite(session->gpsdata.dop.xdop)) {
- fix->epx = session->gpsdata.dop.xdop * h_uere;
- }
- if (0 == isfinite(newfix->epy) &&
- 0 != isfinite(session->gpsdata.dop.ydop)) {
- fix->epy = session->gpsdata.dop.ydop * h_uere;
- }
- if (MODE_3D <= fix->mode &&
- 0 == isfinite(fix->epv) &&
- 0 != isfinite(session->gpsdata.dop.vdop)) {
- fix->epv = session->gpsdata.dop.vdop * v_uere;
- }
- /* 2D error */
- if (0 == isfinite(fix->eph) &&
- 0 != isfinite(session->gpsdata.dop.hdop)) {
- fix->eph = session->gpsdata.dop.hdop * p_uere;
- }
- /* 3D error */
- if (0 == isfinite(fix->sep) &&
- 0 != isfinite(session->gpsdata.dop.pdop)) {
- fix->sep = session->gpsdata.dop.pdop * p_uere;
- }
- /*
- * If we have a current fix and an old fix, and the packet handler
- * didn't set the speed error, climb error or track error members
- * itself, try to compute them now.
- */
- #define EMAX(x, y) (((x) > (y)) ? (x) : (y))
- if (0 < deltatime &&
- MODE_2D <= oldfix->mode) {
- if (0 == isfinite(newfix->eps) &&
- 0 != isfinite(oldfix->epx) &&
- 0 != isfinite(oldfix->epy)) {
- fix->eps = (EMAX(oldfix->epx, oldfix->epy) +
- EMAX(fix->epx, fix->epy)) / deltatime;
- }
- if (0 == isfinite(fix->epd)) {
- /*
- * We compute a track error estimate solely from the
- * position of this fix and the last one. The maximum
- * track error, as seen from the position of last fix, is
- * the angle subtended by the two most extreme possible
- * error positions of the current fix; the expected track
- * error is half that. Let the position of the old fix be
- * A and of the new fix B. We model the view from A as
- * two right triangles ABC and ABD with BC and BD both
- * having the length of the new fix's estimated error.
- * adj = len(AB), opp = len(BC) = len(BD), hyp = len(AC) =
- * len(AD). This leads to spurious uncertainties
- * near 180 when we're moving slowly; to avoid reporting
- * garbage, throw back NaN if the distance from the previous
- * fix is less than the error estimate.
- */
- double adj = earth_distance(oldfix->latitude, oldfix->longitude,
- fix->latitude, fix->longitude);
- double opp = EMAX(fix->epx, fix->epy);
- if (isfinite(adj) != 0 && adj > opp) {
- double hyp = sqrt(adj * adj + opp * opp);
- fix->epd = RAD_2_DEG * 2 * asin(opp / hyp);
- }
- }
- if (0 == isfinite(newfix->epc) &&
- 0 != isfinite(fix->epv) &&
- 0 != isfinite(oldfix->epv)) {
- /* Is this really valid? */
- /* if vertical uncertainties are zero this will be too */
- fix->epc = (oldfix->epv + fix->epv) / deltatime;
- }
- }
- }
- #ifdef __UNUSED__
- {
- // Debug code.
- char tbuf[JSON_DATE_MAX+1];
- GPSD_LOG(&session->context->errout, 0,
- "DEBUG: %s deltatime %.3f, speed %0.3f climb %.3f "
- "epc %.3f fixHAE %.3f oldHAE %.3f\n",
- timespec_to_iso8601(fix->time, tbuf, sizeof(tbuf)),
- deltatime, fix->speed, fix->climb, fix->epc,
- fix->altHAE, oldfix->altHAE);
- }
- #endif // __UNUSED__
- if (0 < fix->time.tv_sec) {
- /* save lastfix, not yet oldfix, for later error computations */
- *lastfix = *fix;
- }
- }
- /* await data from any socket in the all_fds set */
- int gpsd_await_data(fd_set *rfds,
- fd_set *efds,
- const int maxfd,
- fd_set *all_fds,
- struct gpsd_errout_t *errout)
- {
- int status;
- FD_ZERO(efds);
- *rfds = *all_fds;
- GPSD_LOG(LOG_RAW + 1, errout, "select waits, maxfd %d\n", maxfd);
- /*
- * Poll for user commands or GPS data. The timeout doesn't
- * actually matter here since select returns whenever one of
- * the file descriptors in the set goes ready. The point
- * of tracking maxfd is to keep the set of descriptors that
- * pselect(2) has to poll here as small as possible (for
- * low-clock-rate SBCs and the like).
- *
- * As used here, there is no difference between pselect()
- * or select(). No timeout is used, this cuts power consumption,
- * but can lead to infinite hang.
- */
- errno = 0;
- status = pselect(maxfd + 1, rfds, NULL, NULL, NULL, NULL);
- if (status == -1) {
- if (errno == EINTR)
- return AWAIT_NOT_READY;
- else if (errno == EBADF) {
- int fd;
- for (fd = 0; fd < (int)FD_SETSIZE; fd++)
- /*
- * All we care about here is a cheap, fast, uninterruptible
- * way to check if a file descriptor is valid.
- */
- if (FD_ISSET(fd, all_fds) && fcntl(fd, F_GETFL, 0) == -1) {
- FD_CLR(fd, all_fds);
- FD_SET(fd, efds);
- }
- return AWAIT_NOT_READY;
- } else {
- GPSD_LOG(LOG_ERROR, errout, "select: %s\n", strerror(errno));
- return AWAIT_FAILED;
- }
- }
- if (errout->debug >= LOG_SPIN) {
- int i;
- char dbuf[BUFSIZ];
- timespec_t ts_now;
- char ts_str[TIMESPEC_LEN];
- dbuf[0] = '\0';
- for (i = 0; i < (int)FD_SETSIZE; i++)
- if (FD_ISSET(i, all_fds))
- str_appendf(dbuf, sizeof(dbuf), "%d ", i);
- str_rstrip_char(dbuf, ' ');
- (void)strlcat(dbuf, "} -> {", sizeof(dbuf));
- for (i = 0; i < (int)FD_SETSIZE; i++)
- if (FD_ISSET(i, rfds))
- str_appendf(dbuf, sizeof(dbuf), " %d ", i);
- (void)clock_gettime(CLOCK_REALTIME, &ts_now);
- GPSD_LOG(LOG_SPIN, errout,
- "pselect() {%s} at %s (errno %d)\n",
- dbuf,
- timespec_str(&ts_now, ts_str, sizeof(ts_str)),
- errno);
- }
- return AWAIT_GOT_INPUT;
- }
- static bool hunt_failure(struct gps_device_t *session)
- /* after a bad packet, what should cue us to go to next autobaud setting? */
- {
- /*
- * We have tried three different tests here.
- *
- * The first was session->badcount++>1. This worked very well on
- * ttys for years and years, but caused failure to sync on TCP/IP
- * sources, which have I/O boundaries in mid-packet more often
- * than RS232 ones. There's a test for this at
- * test/daemon/tcp-torture.log.
- *
- * The second was session->badcount++>1 && session->lexer.state==0.
- * Fail hunt only if we get a second consecutive bad packet
- * and the lexer is in ground state. We don't want to fail on
- * a first bad packet because the source might have a burst of
- * leading garbage after open. We don't want to fail if the
- * lexer is not in ground state, because that means the read
- * might have picked up a valid partial packet - better to go
- * back around the loop and pick up more data.
- *
- * The "&& session->lexer.state==0" guard causes an intermittent
- * hang while autobauding on SiRF IIIs (but not on SiRF-IIs, oddly
- * enough). Removing this conjunct resurrected the failure
- * of test/daemon/tcp-torture.log.
- *
- * Our third attempt, isatty(session->gpsdata.gps_fd) != 0
- * && session->badcount++>1, reverts to the old test that worked
- * well on ttys for ttys and prevents non-tty devices from *ever*
- * having hunt failures. This has the cost that non-tty devices
- * will never get kicked off for presenting bad packets.
- *
- * This test may need further revision.
- */
- return isatty(session->gpsdata.gps_fd) != 0 && session->badcount++>1;
- }
- /* update the stuff in the scoreboard structure */
- gps_mask_t gpsd_poll(struct gps_device_t *session)
- {
- ssize_t newlen;
- bool driver_change = false;
- timespec_t ts_now;
- timespec_t delta;
- char ts_buf[TIMESPEC_LEN];
- gps_clear_fix(&session->newdata);
- /*
- * Input just became available from a sensor, but no read from the
- * device has yet been done.
- *
- * What we actually do here is trickier. For latency-timing
- * purposes, we want to know the time at the start of the current
- * recording cycle. We rely on the fact that even at 4800bps
- * there's a quiet time perceptible to the human eye in gpsmon
- * between when the last character of the last packet in a
- * 1-second cycle ships and when the next reporting cycle
- * ships. Because the cycle time is fixed, higher baud rates will
- * make this gap larger.
- *
- * Thus, we look for an inter-character delay much larger than an
- * average 4800bps sentence time. How should this delay be set? Well,
- * counting framing bits and erring on the side of caution, it's
- * about 480 characters per second or 2083 microeconds per character;
- * that's almost exactly 0.125 seconds per average 60-char sentence.
- * Doubling this to avoid false positives, we look for an inter-character
- * delay of greater than 0.250s.
- *
- * The above assumes a cycle time of 1 second. To get the minimum size of
- * the quiet period, we multiply by the device cycle time.
- *
- * We can sanity-check these calculation by watching logs. If we have set
- * MINIMUM_QUIET_TIME correctly, the "transmission pause" message below
- * will consistently be emitted just before the sentence that shows up
- * as start-of-cycle in gpsmon, and never emitted at any other point
- * in the cycle.
- *
- * In practice, it seems that edge detection succeeds at 9600bps but
- * fails at 4800bps. This is not surprising, as previous profiling has
- * indicated that at 4800bps some devices overrun a 1-second cycle time
- * with the data they transmit.
- */
- #define MINIMUM_QUIET_TIME 0.25
- if (session->lexer.outbuflen == 0) {
- /* beginning of a new packet */
- (void)clock_gettime(CLOCK_REALTIME, &ts_now);
- if (NULL != session->device_type &&
- (0 < session->lexer.start_time.tv_sec ||
- 0 < session->lexer.start_time.tv_nsec)) {
- const double min_cycle = TSTONS(&session->device_type->min_cycle);
- double quiet_time = (MINIMUM_QUIET_TIME * min_cycle);
- double gap;
- gap = TS_SUB_D(&ts_now, &session->lexer.start_time);
- if (gap > min_cycle)
- GPSD_LOG(LOG_WARN, &session->context->errout,
- "cycle-start detector failed.\n");
- else if (gap > quiet_time) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "transmission pause of %f\n", gap);
- session->sor = ts_now;
- session->lexer.start_char = session->lexer.char_counter;
- }
- }
- session->lexer.start_time = ts_now;
- }
- if (session->lexer.type >= COMMENT_PACKET) {
- session->observed |= PACKET_TYPEMASK(session->lexer.type);
- }
- /* can we get a full packet from the device? */
- if (session->device_type != NULL) {
- newlen = session->device_type->get_packet(session);
- /* coverity[deref_ptr] */
- GPSD_LOG(LOG_RAW, &session->context->errout,
- "%s is known to be %s\n",
- session->gpsdata.dev.path,
- session->device_type->type_name);
- } else {
- newlen = generic_get(session);
- }
- /* update the scoreboard structure from the GPS */
- GPSD_LOG(LOG_RAW + 1, &session->context->errout,
- "%s sent %zd new characters\n",
- session->gpsdata.dev.path, newlen);
- (void)clock_gettime(CLOCK_REALTIME, &ts_now);
- TS_SUB(&delta, &ts_now, &session->gpsdata.online);
- if (newlen < 0) { /* read error */
- GPSD_LOG(LOG_INF, &session->context->errout,
- "GPS on %s returned error %zd (%s sec since data)\n",
- session->gpsdata.dev.path, newlen,
- timespec_str(&delta, ts_buf, sizeof(ts_buf)));
- session->gpsdata.online.tv_sec = 0;
- session->gpsdata.online.tv_nsec = 0;
- return ERROR_SET;
- } else if (newlen == 0) { /* zero length read, possible EOF */
- /*
- * Multiplier is 2 to avoid edge effects due to sampling at the exact
- * wrong time...
- */
- if (0 < session->gpsdata.online.tv_sec &&
- // FIXME: do this with integer math...
- TSTONS(&delta) >= (TSTONS(&session->gpsdata.dev.cycle) * 2)) {
- GPSD_LOG(LOG_INF, &session->context->errout,
- "GPS on %s is offline (%s sec since data)\n",
- session->gpsdata.dev.path,
- timespec_str(&delta, ts_buf, sizeof(ts_buf)));
- session->gpsdata.online.tv_sec = 0;
- session->gpsdata.online.tv_nsec = 0;
- }
- return NODATA_IS;
- } else /* (newlen > 0) */ {
- GPSD_LOG(LOG_RAW, &session->context->errout,
- "packet sniff on %s finds type %d\n",
- session->gpsdata.dev.path, session->lexer.type);
- if (session->lexer.type == COMMENT_PACKET) {
- if (strcmp((const char *)session->lexer.outbuffer, "# EOF\n") == 0) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "synthetic EOF\n");
- return EOF_IS;
- }
- else
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "comment, sync lock deferred\n");
- /* FALL THROUGH */
- } else if (session->lexer.type > COMMENT_PACKET) {
- if (session->device_type == NULL)
- driver_change = true;
- else {
- int newtype = session->lexer.type;
- /*
- * Are we seeing a new packet type? Then we probably
- * want to change drivers.
- */
- bool new_packet_type =
- (newtype != session->device_type->packet_type);
- /*
- * Possibly the old driver has a mode-switcher method, in
- * which case we know it can handle NMEA itself and may
- * want to do special things (like tracking whether a
- * previous mode switch to binary succeeded in suppressing
- * NMEA).
- */
- bool dependent_nmea = (newtype == NMEA_PACKET &&
- session->device_type->mode_switcher != NULL);
- /*
- * Compute whether to switch drivers.
- * If the previous driver type was sticky and this one
- * isn't, we'll revert after processing the packet.
- */
- driver_change = new_packet_type && !dependent_nmea;
- }
- if (driver_change) {
- const struct gps_type_t **dp;
- for (dp = gpsd_drivers; *dp; dp++)
- if (session->lexer.type == (*dp)->packet_type) {
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "switching to match packet type %d: %s\n",
- session->lexer.type, gpsd_prettydump(session));
- (void)gpsd_switch_driver(session, (*dp)->type_name);
- break;
- }
- }
- session->badcount = 0;
- session->gpsdata.dev.driver_mode =
- (session->lexer.type > NMEA_PACKET) ? MODE_BINARY : MODE_NMEA;
- /* FALL THROUGH */
- } else if (hunt_failure(session) && !gpsd_next_hunt_setting(session)) {
- (void)clock_gettime(CLOCK_REALTIME, &ts_now);
- TS_SUB(&delta, &ts_now, &session->gpsdata.online);
- GPSD_LOG(LOG_INF, &session->context->errout,
- "hunt on %s failed (%s sec since data)\n",
- session->gpsdata.dev.path,
- timespec_str(&delta, ts_buf, sizeof(ts_buf)));
- return ERROR_SET;
- }
- }
- if (session->lexer.outbuflen == 0) { /* got new data, but no packet */
- GPSD_LOG(LOG_RAW + 1, &session->context->errout,
- "New data on %s, not yet a packet\n",
- session->gpsdata.dev.path);
- return ONLINE_SET;
- } else { /* we have recognized a packet */
- gps_mask_t received = PACKET_SET;
- (void)clock_gettime(CLOCK_REALTIME, &session->gpsdata.online);
- GPSD_LOG(LOG_RAW + 1, &session->context->errout,
- "Accepted packet on %s.\n",
- session->gpsdata.dev.path);
- /* track the packet count since achieving sync on the device */
- if (driver_change &&
- (session->drivers_identified & (1 << session->driver_index)) == 0) {
- speed_t speed = gpsd_get_speed(session);
- /* coverity[var_deref_op] */
- GPSD_LOG(LOG_INF, &session->context->errout,
- "%s identified as type %s, %ld sec @ %ubps\n",
- session->gpsdata.dev.path,
- session->device_type->type_name,
- (long)(time(NULL) - session->opentime),
- (unsigned int)speed);
- /* fire the init_query method */
- if (session->device_type != NULL
- && session->device_type->init_query != NULL) {
- /*
- * We can force readonly off knowing this method does
- * not alter device state.
- */
- bool saved = session->context->readonly;
- session->context->readonly = false;
- session->device_type->init_query(session);
- session->context->readonly = saved;
- }
- /* fire the identified hook */
- if (session->device_type != NULL
- && session->device_type->event_hook != NULL)
- session->device_type->event_hook(session, event_identified);
- session->lexer.counter = 0;
- /* let clients know about this. */
- received |= DRIVER_IS;
- /* mark the fact that this driver has been seen */
- session->drivers_identified |= (1 << session->driver_index);
- } else
- session->lexer.counter++;
- /* fire the configure hook, on every packet. Seems excessive... */
- if (session->device_type != NULL
- && session->device_type->event_hook != NULL)
- session->device_type->event_hook(session, event_configure);
- /*
- * The guard looks superfluous, but it keeps the rather expensive
- * gpsd_packetdump() function from being called even when the debug
- * level does not actually require it.
- */
- if (session->context->errout.debug >= LOG_RAW)
- GPSD_LOG(LOG_RAW, &session->context->errout,
- "raw packet of type %d, %zd:%s\n",
- session->lexer.type,
- session->lexer.outbuflen,
- gpsd_prettydump(session));
- /* Get data from current packet into the fix structure */
- if (session->lexer.type != COMMENT_PACKET)
- if (session->device_type != NULL
- && session->device_type->parse_packet != NULL)
- received |= session->device_type->parse_packet(session);
- /*
- * We may want to revert to the last driver that was marked
- * sticky. What this accomplishes is that if we've just
- * processed something like AIVDM, but a driver with control
- * methods or an event hook had been active before that, we
- * keep the information about those capabilities.
- */
- if (!STICKY(session->device_type)
- && session->last_controller != NULL
- && STICKY(session->last_controller)) {
- session->device_type = session->last_controller;
- GPSD_LOG(LOG_PROG, &session->context->errout,
- "reverted to %s driver...\n",
- session->device_type->type_name);
- }
- /* are we going to generate a report? if so, count characters */
- if ((received & REPORT_IS) != 0) {
- session->chars = session->lexer.char_counter -
- session->lexer.start_char;
- }
- session->gpsdata.set = ONLINE_SET | received;
- /*
- * Compute fix-quality data from the satellite positions.
- * These will not overwrite any DOPs reported from the packet
- * we just got.
- */
- if ((received & SATELLITE_SET) != 0
- && session->gpsdata.satellites_visible > 0) {
- session->gpsdata.set |= fill_dop(&session->context->errout,
- &session->gpsdata,
- &session->gpsdata.dop);
- }
- /* copy/merge device data into staging buffers */
- if ((session->gpsdata.set & CLEAR_IS) != 0) {
- /* CLEAR_IS should only be set on first sentence of cycle */
- gps_clear_fix(&session->gpsdata.fix);
- gps_clear_att(&session->gpsdata.attitude);
- }
- /* GPSD_LOG(LOG_PROG, &session->context->errout,
- "transfer mask: %s\n",
- gps_maskdump(session->gpsdata.set)); */
- gps_merge_fix(&session->gpsdata.fix,
- session->gpsdata.set, &session->newdata);
- gpsd_error_model(session);
- /*
- * Count good fixes. We used to check
- * session->gpsdata.fix.status > STATUS_NO_FIX
- * here, but that wasn't quite right. That tells us whether
- * we think we have a valid fix for the current cycle, but remains
- * true while following non-fix packets are received. What we
- * really want to know is whether the last packet received was a
- * fix packet AND held a valid fix. We must ignore non-fix packets
- * AND packets which have fix data but are flagged as invalid. Some
- * devices output fix packets on a regular basis, even when unable
- * to derive a good fix. Such packets should set STATUS_NO_FIX.
- */
- if (0 != (session->gpsdata.set & (LATLON_SET|ECEF_SET))) {
- if ( session->gpsdata.fix.status > STATUS_NO_FIX) {
- session->context->fixcnt++;
- session->fixcnt++;
- } else {
- session->context->fixcnt = 0;
- session->fixcnt = 0;
- }
- }
- /*
- * Sanity check. This catches a surprising number of port and
- * driver errors, including 32-vs.-64-bit problems.
- */
- if ((session->gpsdata.set & TIME_SET) != 0) {
- if (session->newdata.time.tv_sec >
- (time(NULL) + (60 * 60 * 24 * 365))) {
- GPSD_LOG(LOG_WARN, &session->context->errout,
- "date (%lld) more than a year in the future!\n",
- (long long)session->newdata.time.tv_sec);
- } else if (session->newdata.time.tv_sec < 0) {
- GPSD_LOG(LOG_ERROR, &session->context->errout,
- "date (%lld) is negative!\n",
- (long long)session->newdata.time.tv_sec);
- }
- }
- return session->gpsdata.set;
- }
- /* Should never get here */
- GPSD_LOG(LOG_EMERG, &session->context->errout,
- "fell out of gps_poll()!\n");
- return 0;
- }
- /* consume and handle packets from a specified device */
- int gpsd_multipoll(const bool data_ready,
- struct gps_device_t *device,
- void (*handler)(struct gps_device_t *, gps_mask_t),
- float reawake_time)
- {
- if (data_ready)
- {
- int fragments;
- GPSD_LOG(LOG_RAW + 1, &device->context->errout,
- "polling %d\n", device->gpsdata.gps_fd);
- #ifdef NETFEED_ENABLE
- /*
- * Strange special case - the opening transaction on an NTRIP
- * connection * may not yet be completed.
- * Try to ratchet things forward.
- */
- if (device->servicetype == service_ntrip
- && device->ntrip.conn_state != ntrip_conn_established) {
- (void)ntrip_open(device, "");
- if (device->ntrip.conn_state == ntrip_conn_err) {
- GPSD_LOG(LOG_WARN, &device->context->errout,
- "connection to ntrip server failed\n");
- device->ntrip.conn_state = ntrip_conn_init;
- return DEVICE_ERROR;
- } else {
- return DEVICE_READY;
- }
- }
- #endif /* NETFEED_ENABLE */
- for (fragments = 0; ; fragments++) {
- gps_mask_t changed = gpsd_poll(device);
- if (changed == EOF_IS) {
- GPSD_LOG(LOG_WARN, &device->context->errout,
- "device signed off %s\n",
- device->gpsdata.dev.path);
- return DEVICE_EOF;
- } else if (changed == ERROR_SET) {
- GPSD_LOG(LOG_WARN, &device->context->errout,
- "device read of %s returned error or "
- "packet sniffer failed sync (flags %s)\n",
- device->gpsdata.dev.path,
- gps_maskdump(changed));
- return DEVICE_ERROR;
- } else if (changed == NODATA_IS) {
- /*
- * No data on the first fragment read means the device
- * fd may have been in an end-of-file condition on select.
- */
- if (fragments == 0) {
- GPSD_LOG(LOG_DATA, &device->context->errout,
- "%s returned zero bytes\n",
- device->gpsdata.dev.path);
- if (device->zerokill) {
- /* failed timeout-and-reawake, kill it */
- gpsd_deactivate(device);
- if (device->ntrip.works) {
- // reset so we try this once only
- device->ntrip.works = false;
- if (gpsd_activate(device, O_CONTINUE) < 0) {
- GPSD_LOG(LOG_WARN, &device->context->errout,
- "reconnect to ntrip server failed\n");
- return DEVICE_ERROR;
- } else {
- GPSD_LOG(LOG_INF, &device->context->errout,
- "reconnecting to ntrip server\n");
- return DEVICE_READY;
- }
- }
- } else if (reawake_time == 0) {
- return DEVICE_ERROR;
- } else {
- /*
- * Disable listening to this fd for long enough
- * that the buffer can fill up again.
- */
- GPSD_LOG(LOG_DATA, &device->context->errout,
- "%s will be repolled in %f seconds\n",
- device->gpsdata.dev.path, reawake_time);
- device->reawake = time(NULL) + reawake_time;
- return DEVICE_UNREADY;
- }
- }
- /*
- * No data on later fragment reads just means the
- * input buffer is empty. In this case break out
- * of the fragment-processing loop but consider
- * the device still good.
- */
- break;
- }
- /* we got actual data, head off the reawake special case */
- device->zerokill = false;
- device->reawake = (time_t)0;
- /* must have a full packet to continue */
- if ((changed & PACKET_SET) == 0)
- break;
- /* conditional prevents mask dumper from eating CPU */
- if (device->context->errout.debug >= LOG_DATA) {
- if (device->lexer.type == BAD_PACKET)
- GPSD_LOG(LOG_DATA, &device->context->errout,
- "packet with bad checksum from %s\n",
- device->gpsdata.dev.path);
- else
- GPSD_LOG(LOG_DATA, &device->context->errout,
- "packet type %d from %s with %s\n",
- device->lexer.type,
- device->gpsdata.dev.path,
- gps_maskdump(device->gpsdata.set));
- }
- /* handle data contained in this packet */
- if (device->lexer.type != BAD_PACKET)
- handler(device, changed);
- #ifdef __future__
- // this breaks: test/daemon/passthrough.log ??
- /*
- * Bernd Ocklin suggests:
- * Exit when a full packet was received and parsed.
- * This allows other devices to be serviced even if
- * this device delivers a full packet at every single
- * read.
- * Otherwise we can sit here for a long time without
- * any for-loop exit condition being met.
- * It might also reduce the latency from a received packet to
- * it being output by gpsd.
- */
- if ((changed & PACKET_SET) != 0)
- break;
- #endif /* __future__ */
- }
- }
- else if (device->reawake>0 && time(NULL) >device->reawake) {
- /* device may have had a zero-length read */
- GPSD_LOG(LOG_DATA, &device->context->errout,
- "%s reawakened after zero-length read\n",
- device->gpsdata.dev.path);
- device->reawake = (time_t)0;
- device->zerokill = true;
- return DEVICE_READY;
- }
- /* no change in device descriptor state */
- return DEVICE_UNCHANGED;
- }
- /* end-of-session wrapup */
- void gpsd_wrap(struct gps_device_t *session)
- {
- if (!BAD_SOCKET(session->gpsdata.gps_fd))
- gpsd_deactivate(session);
- }
- void gpsd_zero_satellites( struct gps_data_t *out)
- {
- int sat;
- (void)memset(out->skyview, '\0', sizeof(out->skyview));
- out->satellites_visible = 0;
- /* zero is good inbound data for ss, elevation, and azimuth. */
- /* we need to set them to invalid values */
- for ( sat = 0; sat < MAXCHANNELS; sat++ ) {
- out->skyview[sat].azimuth = NAN;
- out->skyview[sat].elevation = NAN;
- out->skyview[sat].ss = NAN;
- out->skyview[sat].freqid = -1;
- }
- #if 0
- /*
- * We used to clear DOPs here, but this causes misbehavior on some
- * combined GPS/GLONASS/QZSS receivers like the Telit SL869; the
- * symptom is that the "satellites_used" field in a struct gps_data_t
- * filled in by gps_read() is always zero.
- */
- gps_clear_dop(&out->dop);
- #endif
- }
- /* Latch the fact that we've saved a fix.
- * And add in the device fudge */
- void ntp_latch(struct gps_device_t *device, struct timedelta_t *td)
- {
- /* this should be an invariant of the way this function is called */
- if (0 >= device->newdata.time.tv_sec) {
- return;
- }
- (void)clock_gettime(CLOCK_REALTIME, &td->clock);
- /* structure copy of time from GPS */
- td->real = device->newdata.time;
- /* is there an offset method? */
- if (NULL != device->device_type &&
- NULL != device->device_type->time_offset) {
- double integral;
- double offset = device->device_type->time_offset(device);
- /* add in offset which is double */
- td->real.tv_nsec += (long)(modf(offset, &integral) * 1e9);
- td->real.tv_sec += (time_t)integral;
- TS_NORM(&td->real);
- }
- /* thread-safe update */
- pps_thread_fixin(&device->pps_thread, td);
- }
- /* end */
- // vim: set expandtab shiftwidth=4
|