Example #1
0
int
main(int argc, char **argv)
{
    int fd;
    pps_info_t pi;
    pps_params_t pp;
    pps_handle_t ph;
    int i, mode;
    u_int olda, oldc;
    double d = 0;
    struct timespec to;

    if (argc < 2)
        argv[1] = "/dev/cuaa1";
    setbuf(stdout, 0);
    fd = open(argv[1], O_RDONLY);
    if (fd < 0)
        err(1, argv[1]);
    i = time_pps_create(fd, &ph);
    if (i < 0)
        err(1, "time_pps_create");

    i = time_pps_getcap(ph, &mode);
    if (i < 0)
        err(1, "time_pps_getcap");

    pp.mode = PPS_CAPTUREASSERT | PPS_ECHOASSERT;
    pp.mode = PPS_CAPTUREBOTH;
    /* pp.mode = PPS_CAPTUREASSERT; */

    i = time_pps_setparams(ph, &pp);
    if (i < 0)
        err(1, "time_pps_setparams");

    while (1) {
        to.tv_nsec = 0;
        to.tv_sec = 0;
        i = time_pps_fetch(ph, PPS_TSFMT_TSPEC, &pi, &to);
        if (i < 0)
            err(1, "time_pps_fetch");
        if (olda == pi.assert_sequence &&
                oldc == pi.clear_sequence) {
            usleep(10000);
            continue;
        }

        Chew(&pi.assert_timestamp, &pi.clear_timestamp,
             pi.assert_sequence, pi.clear_sequence);
        olda = pi.assert_sequence;
        oldc = pi.clear_sequence;
    }

    return(0);
}
Example #2
0
    int64_t
    PPS::fetch(double timeout)
    {
      // Linux v2.6 (LinuxPPS) implementation.
#if defined(DUNE_SYS_HAS_TIMEPPS_H)
      timespec tstout = DUNE_TIMESPEC_INIT_SEC_FP(timeout);
      pps_info_t bfr = {0};

      int rv = time_pps_fetch(m_handle, PPS_TSFMT_TSPEC, &bfr, &tstout);

      if (rv < 0)
        return -1;

      return (int64_t)bfr.assert_timestamp.tv_sec * Time::c_nsec_per_sec
             + (int64_t)bfr.assert_timestamp.tv_nsec;

      // Not yet implemented.
#else
      (void)timeout;

      return -1;
#endif
    }
Example #3
0
/*
 * refclock_pps - called once per second
 *
 * This routine is called once per second. It snatches the PPS
 * timestamp from the kernel and saves the sign-extended fraction in
 * a circular buffer for processing at the next poll event.
 */
int
refclock_pps(
	struct peer *peer,		/* peer structure pointer */
	struct refclock_atom *ap,	/* atom structure pointer */
	int	mode			/* mode bits */	
	)
{
	struct refclockproc *pp;
	pps_info_t pps_info;
	struct timespec timeout;
	double	dtemp;

	/*
	 * We require the clock to be synchronized before setting the
	 * parameters. When the parameters have been set, fetch the
	 * most recent PPS timestamp.
	 */ 
	pp = peer->procptr;
	if (ap->handle == 0)
		return (0);

	if (ap->pps_params.mode == 0 && sys_leap != LEAP_NOTINSYNC) {
		if (refclock_params(pp->sloppyclockflag, ap) < 1)
			return (0);
	}
	timeout.tv_sec = 0;
	timeout.tv_nsec = 0;
	memset(&pps_info, 0, sizeof(pps_info_t));
	if (time_pps_fetch(ap->handle, PPS_TSFMT_TSPEC, &pps_info,
	    &timeout) < 0) {
		refclock_report(peer, CEVNT_FAULT);
		return (0);
	}
	timeout = ap->ts;
	if (ap->pps_params.mode & PPS_CAPTUREASSERT)
		ap->ts = pps_info.assert_timestamp;
	else if (ap->pps_params.mode & PPS_CAPTURECLEAR)
		ap->ts = pps_info.clear_timestamp;
	else
		return (0);
	
	/*
	 * There can be zero, one or two PPS pulses between polls,
	 * depending on the poll interval relative to the PPS interval.
	 * The pulse must be newer and within the range gate relative
	 * to the last pulse.
	 */
	if (ap->ts.tv_sec <= timeout.tv_sec || abs(ap->ts.tv_nsec -
	    timeout.tv_nsec) > RANGEGATE)
		return (0);

	/*
	 * Convert to signed fraction offset and stuff in median filter.
	 */
	pp->lastrec.l_ui = (u_int32)ap->ts.tv_sec + JAN_1970;
	dtemp = ap->ts.tv_nsec / 1e9;
	pp->lastrec.l_uf = (u_int32)(dtemp * FRAC);
	if (dtemp > .5)
		dtemp -= 1.;
	SAMPLE(-dtemp + pp->fudgetime1);
#ifdef DEBUG
	if (debug > 1)
		printf("refclock_pps: %lu %f %f\n", current_time,
		    dtemp, pp->fudgetime1);
#endif
	return (1);
}
Example #4
0
/*@-mustfreefresh -type -unrecog -branchstate@*/
static /*@null@*/ void *gpsd_ppsmonitor(void *arg)
{
    struct gps_device_t *session = (struct gps_device_t *)arg;
    double last_fixtime_real = 0, last_fixtime_clock = 0;
#ifndef HAVE_CLOCK_GETTIME
    struct timeval  clock_tv = {0, 0};
#endif /* HAVE_CLOCK_GETTIME */
    struct timespec clock_ts = {0, 0};
    time_t last_second_used = 0;
#if defined(TIOCMIWAIT)
    int cycle, duration; 
    /* state is the last state of the tty control ssignals */
    int state = 0, unchanged = 0;
    /* state_last is previous state */
    int state_last = 0;
    /* pulse stores the time of the last two edges */
    struct timespec pulse[2] = { {0, 0}, {0, 0} };
    /* edge, used as index into pulse to find previous edges */
    int edge = 0;       /* 0 = clear edge, 1 = assert edge */
#endif /* TIOCMIWAIT */
#if defined(HAVE_SYS_TIMEPPS_H)
    int edge_kpps = 0;       /* 0 = clear edge, 1 = assert edge */
#ifndef S_SPLINT_S
    int cycle_kpps, duration_kpps;
    /* kpps_pulse stores the time of the last two edges */
    struct timespec pulse_kpps[2] = { {0, 0}, {0, 0} };
    struct timespec ts_kpps;
    pps_info_t pi;

    memset( (void *)&pi, 0, sizeof(pps_info_t));
#endif /* S_SPLINT_S */
#endif /* defined(HAVE_SYS_TIMEPPS_H) */

    /*
     * Wait for status change on any handshake line.  Just one edge,
     * we do not want to be spinning waiting for the trailing edge of
     * a pulse. The only assumption here is that no GPS lights up more
     * than one of these pins.  By waiting on all of them we remove a
     * configuration switch. 
     *
     * Once we have the latest edge we compare it to the last edge which we
     * stored.  If the edge passes sanity checks we use it to send to
     * ntpshm and chrony_send
     */

    while (session->thread_report_hook != NULL 
           || session->context->pps_hook != NULL) {
	bool ok = false;
#if defined(HAVE_SYS_TIMEPPS_H)
	// cppcheck-suppress variableScope
	bool ok_kpps = false;
#endif /* HAVE_SYS_TIMEPPS_H */
	char *log = NULL;

#if defined(TIOCMIWAIT)
        /* we are lucky to have TIOCMIWAIT, so wait for next edge */
#define PPS_LINE_TIOC (TIOCM_CD|TIOCM_CAR|TIOCM_RI|TIOCM_CTS)
        if (ioctl(session->gpsdata.gps_fd, TIOCMIWAIT, PPS_LINE_TIOC) != 0) {
	    gpsd_report(session->context->debug, LOG_ERROR,
			"PPS ioctl(TIOCMIWAIT) failed: %d %.40s\n",
			errno, strerror(errno));
	    break;
	}
        /* quick, grab a copy of last_fixtime before it changes */
	last_fixtime_real = session->last_fixtime.real;
	last_fixtime_clock = session->last_fixtime.clock;

/*@-noeffect@*/
        /* get the time after we just woke up */
#ifdef HAVE_CLOCK_GETTIME
	/* using  clock_gettime() here, that is nSec,
	 * not uSec like gettimeofday */
	if ( 0 > clock_gettime(CLOCK_REALTIME, &clock_ts) ) {
	    /* uh, oh, can not get time! */
	    gpsd_report(session->context->debug, LOG_ERROR,
			"PPS clock_gettime() failed\n");
	    break;
	}
#else
	if ( 0 > gettimeofday(&clock_tv, NULL) ) {
	    /* uh, oh, can not get time! */
	    gpsd_report(session->context->debug, LOG_ERROR,
			"PPS gettimeofday() failed\n");
	    break;
	}
	TVTOTS( &clock_ts, &clock_tv);
#endif /* HAVE_CLOCK_GETTIME */
/*@+noeffect@*/

        /* got the edge, got the time just after the edge, now quickly
         * get the edge state */
	/*@ +ignoresigns */
	if (ioctl(session->gpsdata.gps_fd, TIOCMGET, &state) != 0) {
	    gpsd_report(session->context->debug, LOG_ERROR,
			"PPS ioctl(TIOCMGET) failed\n");
	    break;
	}
	/*@ -ignoresigns */

	/* mask for monitored lines */
	state &= PPS_LINE_TIOC;
	edge = (state > state_last) ? 1 : 0;
#endif /* TIOCMIWAIT */

	/* ok and log used by KPPS and TIOCMIWAIT */
	// cppcheck-suppress redundantAssignment
	ok = false;  
	log = NULL;  
#if defined(HAVE_SYS_TIMEPPS_H) && !defined(S_SPLINT_S)
        if ( 0 <= session->kernelpps_handle ) {
	    struct timespec kernelpps_tv;
	    /* on a quad core 2.4GHz Xeon using KPPS timestamp instead of plain 
             * PPS timestamp removes about 20uS of latency, and about +/-5uS 
             * of jitter 
             */
#ifdef TIOCMIWAIT
	    /*
	     * We use of a non-NULL zero timespec here,
	     * which means to return immediately with -1 (section
	     * 3.4.3).  This is because we know we just got a pulse because 
             * TIOCMIWAIT just woke up.
	     * The timestamp has already been captured in the kernel, and we 
             * are merely fetching it here.
	     */
            memset( (void *)&kernelpps_tv, 0, sizeof(kernelpps_tv));
#else /* not TIOCMIWAIT */
	    /*
	     * RFC2783 specifies that a NULL timeval means to wait.
             *
             * FIXME, this will fail on 2Hz 'PPS', maybe should wait 3 Sec.
	     */
	    kernelpps_tv.tv_sec = 1;
	    kernelpps_tv.tv_nsec = 0;
#endif
	    if ( 0 > time_pps_fetch(session->kernelpps_handle, PPS_TSFMT_TSPEC
	        , &pi, &kernelpps_tv)) {
		gpsd_report(session->context->debug, LOG_ERROR,
			    "KPPS kernel PPS failed\n");
	    } else {
		// find the last edge
		// FIXME a bit simplistic, should hook into the
                // cycle/duration check below.
	    	if ( pi.assert_timestamp.tv_sec > pi.clear_timestamp.tv_sec ) {
		    edge_kpps = 1;
		    ts_kpps = pi.assert_timestamp;
	    	} else if ( pi.assert_timestamp.tv_sec < pi.clear_timestamp.tv_sec ) {
		    edge_kpps = 0;
		    ts_kpps = pi.clear_timestamp;
		} else if ( pi.assert_timestamp.tv_nsec > pi.clear_timestamp.tv_nsec ) {
		    edge_kpps = 1;
		    ts_kpps = pi.assert_timestamp;
		} else {
		    edge_kpps = 0;
		    ts_kpps = pi.clear_timestamp;
		}
		/*
		 * pps_seq_t is uint32_t on NetBSD, so cast to
		 * unsigned long as a wider-or-equal type to
		 * accomodate Linux's type.
		 */
		gpsd_report(session->context->debug, LOG_PROG,
			    "KPPS assert %ld.%09ld, sequence: %ld - "
			    "clear  %ld.%09ld, sequence: %ld\n",
			    pi.assert_timestamp.tv_sec,
			    pi.assert_timestamp.tv_nsec,
			    (unsigned long) pi.assert_sequence,
			    pi.clear_timestamp.tv_sec,
			    pi.clear_timestamp.tv_nsec,
			    (unsigned long) pi.clear_sequence);
		gpsd_report(session->context->debug, LOG_PROG,
			    "KPPS data: using %s\n",
			    edge_kpps ? "assert" : "clear");

	        cycle_kpps = timespec_diff_ns(ts_kpps, pulse_kpps[edge_kpps])/1000;
	        duration_kpps = timespec_diff_ns(ts_kpps, pulse_kpps[(int)(edge_kpps == 0)])/1000;
	        gpsd_report(session->context->debug, LOG_PROG,
		    "KPPS cycle: %7d uSec, duration: %7d uSec @ %lu.%09lu\n",
		    cycle_kpps, duration_kpps,
		    (unsigned long)ts_kpps.tv_sec,
		    (unsigned long)ts_kpps.tv_nsec);
		pulse_kpps[edge_kpps] = ts_kpps;
		if (990000 < cycle_kpps && 1010000 > cycle_kpps) {
		    /* KPPS passes a basic sanity check */
		    ok_kpps = true;
		    log = "KPPS";
		}
	    }
	}
#endif /* defined(HAVE_SYS_TIMEPPS_H) && !defined(S_SPLINT_S) */

#if defined(TIOCMIWAIT)
	/*@ +boolint @*/
	cycle = timespec_diff_ns(clock_ts, pulse[edge]) / 1000;
	duration = timespec_diff_ns(clock_ts, pulse[(int)(edge == 0)])/1000;
	/*@ -boolint @*/
	if (state == state_last) {
	    /* some pulses may be so short that state never changes */
	    if (999000 < cycle && 1001000 > cycle) {
		duration = 0;
		unchanged = 0;
		gpsd_report(session->context->debug, LOG_RAW,
			    "PPS pps-detect on %s invisible pulse\n",
			    session->gpsdata.dev.path);
	    } else if (++unchanged == 10) {
                /* not really unchanged, just out of bounds */
		unchanged = 1;
		gpsd_report(session->context->debug, LOG_WARN,
			    "PPS TIOCMIWAIT returns unchanged state, ppsmonitor sleeps 10\n");
		(void)sleep(10);
	    }
	} else {
	    gpsd_report(session->context->debug, LOG_RAW,
			"PPS pps-detect on %s changed to %d\n",
			session->gpsdata.dev.path, state);
	    unchanged = 0;
	}
	state_last = state;
        /* save this edge so we know next cycle time */
	pulse[edge] = clock_ts;
	gpsd_report(session->context->debug, LOG_PROG,
		    "PPS edge: %d, cycle: %7d uSec, duration: %7d uSec @ %lu.%09lu\n",
		    edge, cycle, duration,
		    (unsigned long)clock_ts.tv_sec,
		    (unsigned long)clock_ts.tv_nsec);
	if (unchanged) {
	    // strange, try again
	    continue;
	}

	/*
	 * The PPS pulse is normally a short pulse with a frequency of
	 * 1 Hz, and the UTC second is defined by the front edge. But we
	 * don't know the polarity of the pulse (different receivers
	 * emit different polarities). The duration variable is used to
	 * determine which way the pulse is going. The code assumes
	 * that the UTC second is changing when the signal has not
	 * been changing for at least 800ms, i.e. it assumes the duty
	 * cycle is at most 20%.
	 *
	 * Some GPSes instead output a square wave that is 0.5 Hz and each
	 * edge denotes the start of a second.
	 *
	 * Some GPSes, like the Globalsat MR-350P, output a 1uS pulse.
	 * The pulse is so short that TIOCMIWAIT sees a state change
	 * but by the time TIOCMGET is called the pulse is gone.
	 *
	 * A few stupid GPSes, like the Furuno GPSClock, output a 1.0 Hz
	 * square wave where the leading edge is the start of a second
	 *
	 * 5Hz GPS (Garmin 18-5Hz) pulses at 5Hz. Set the pulse length to
	 * 40ms which gives a 160ms pulse before going high.
	 *
	 */

	log = "Unknown error";
        if ( 0 > cycle ) {
	    log = "Rejecting negative cycle\n";
	} else if (199000 > cycle) {
	    // too short to even be a 5Hz pulse
	    log = "Too short for 5Hz\n";
	} else if (201000 > cycle) {
	    /* 5Hz cycle */
	    /* looks like 5hz PPS pulse */
	    if (100000 > duration) {
		/* BUG: how does the code know to tell ntpd
		 * which 1/5 of a second to use?? */
		ok = true;
		log = "5Hz PPS pulse\n";
	    }
	} else if (999000 > cycle) {
	    log = "Too long for 5Hz, too short for 1Hz\n";
	} else if (1001000 > cycle) {
	    /* looks like PPS pulse or square wave */
	    if (0 == duration) {
		ok = true;
		log = "invisible pulse\n";
	    } else if (499000 > duration) {
		/* end of the short "half" of the cycle */
		/* aka the trailing edge */
		log = "1Hz trailing edge\n";
	    } else if (501000 > duration) {
		/* looks like 1.0 Hz square wave, ignore trailing edge */
		if (edge == 1) {
		    ok = true;
		    log = "square\n";
		}
	    } else {
		/* end of the long "half" of the cycle */
		/* aka the leading edge */
		ok = true;
		log = "1Hz leading edge\n";
	    }
	} else if (1999000 > cycle) {
	    log = "Too long for 1Hz, too short for 2Hz\n";
	} else if (2001000 > cycle) {
	    /* looks like 0.5 Hz square wave */
	    if (999000 > duration) {
		log = "0.5 Hz square too short duration\n";
	    } else if (1001000 > duration) {
		ok = true;
		log = "0.5 Hz square wave\n";
	    } else {
		log = "0.5 Hz square too long duration\n";
	    }
	} else {
	    log = "Too long for 0.5Hz\n";
	}
#endif /* TIOCMIWAIT */
	if ( ok && last_second_used >= last_fixtime_real ) {
		/* uh, oh, this second already handled */
		ok = 0;
		log = "this second already handled\n";
	}

	if (ok) {
	    /* offset is the skew from expected to observed pulse time */
	    double offset;
	    /* delay after last fix */
	    double delay;
	    char *log1 = NULL;
	    /* drift.real is the time we think the pulse represents  */
	    struct timedrift_t drift;
	    gpsd_report(session->context->debug, LOG_RAW,
			"PPS edge accepted %.100s", log);
#if defined(HAVE_SYS_TIMEPPS_H)
            if ( 0 <= session->kernelpps_handle && ok_kpps) {
		/* use KPPS time */
		/* pick the right edge */
		if ( edge_kpps ) {
		    clock_ts = pi.assert_timestamp; /* structure copy */
		} else {
		    clock_ts = pi.clear_timestamp;  /* structure copy */
		}
	    } 
#endif /* defined(HAVE_SYS_TIMEPPS_H) */
	    /* else, use plain PPS */

            /* This innocuous-looking "+ 1" embodies a significant
             * assumption: that GPSes report time to the second over the
             * serial stream *after* emitting PPS for the top of second.
             * Thus, when we see PPS our available report is from the
             * previous cycle and we must increment. 
             *
             * FIXME! The GR-601W at 38,400 or faster can send the
             * serial fix before PPS by about 10 mSec!
             */

	    /*@+relaxtypes@*/
	    drift.real.tv_sec = last_fixtime_real + 1;
	    drift.real.tv_nsec = 0;  /* need to be fixed for 5Hz */
	    drift.clock = clock_ts;
	    /*@-relaxtypes@*/

	    /* check to see if we have a fresh timestamp from the
	     * GPS serial input then use that */
	    offset = (drift.real.tv_sec - drift.clock.tv_sec);
	    offset += ((drift.real.tv_nsec - drift.clock.tv_nsec) / 1e9);
	    delay = (drift.clock.tv_sec + drift.clock.tv_nsec / 1e9) - last_fixtime_clock;
	    if (0.0 > delay || 1.0 < delay) {
		gpsd_report(session->context->debug, LOG_RAW,
			    "PPS: no current GPS seconds: %f\n",
			    delay);
		log1 = "timestamp out of range";
	    } else {
		/*@-compdef@*/
		last_second_used = last_fixtime_real;
		if (session->thread_report_hook != NULL) 
		    log1 = session->thread_report_hook(session, &drift);
		else
		    log1 = "no report hook";
		if (session->context->pps_hook != NULL)
		    session->context->pps_hook(session, &drift);
		/*@ -unrecog  (splint has no pthread declarations as yet) @*/
		(void)pthread_mutex_lock(&ppslast_mutex);
		/*@ +unrecog @*/
		/*@-type@*/ /* splint is confused about struct timespec */
		session->ppslast = drift;
		/*@+type@*/
		session->ppscount++;
		/*@ -unrecog (splint has no pthread declarations as yet) @*/
		(void)pthread_mutex_unlock(&ppslast_mutex);
		/*@ +unrecog @*/
		/*@+compdef@*/
		/*@-type@*/ /* splint is confused about struct timespec */
		gpsd_report(session->context->debug, LOG_INF,
			    "PPS hooks called with %.20s %lu.%09lu offset %.9f\n",
			    log1,
			    (unsigned long)clock_ts.tv_sec,
			    (unsigned long)clock_ts.tv_nsec,
			    offset);
		/*@+type@*/
            }
	    /*@-type@*/ /* splint is confused about struct timespec */
	    gpsd_report(session->context->debug, LOG_PROG,
		    "PPS edge %.20s %lu.%09lu offset %.9f\n",
		    log1,
		    (unsigned long)clock_ts.tv_sec,
		    (unsigned long)clock_ts.tv_nsec,
		    offset);
	    /*@+type@*/
	} else {
	    gpsd_report(session->context->debug, LOG_RAW,
			"PPS edge rejected %.100s", log);
	}
    }
#if defined(HAVE_SYS_TIMEPPS_H)
    if (session->kernelpps_handle > 0) {
	gpsd_report(session->context->debug, LOG_PROG, 
            "PPS descriptor cleaned up\n");
	(void)time_pps_destroy(session->kernelpps_handle);
    }
#endif
    if (session->thread_wrap_hook != NULL)
	session->thread_wrap_hook(session);
    gpsd_report(session->context->debug, LOG_PROG, 
         "PPS gpsd_ppsmonitor exited.\n");
    return NULL;
}
Example #5
0
int
serWaitForSerialChange ( serDevT* dev )
{
	struct timeval tv;
	time_f	timef;
	int	i,ret;
#ifdef ENABLE_TIMEPPS
	struct timespec timeout;
	pps_info_t	ppsinfo;
	int		ppslines;
#endif
#ifdef ENABLE_GPIO
	struct pollfd  pollfds[1];
#endif

	if ( dev->modemlines == 0 )
		return -1;

	switch ( dev->mode )
	{
	case SERPORT_MODE_POLL:

		for ( i=0; i<10*1000; i++ )
		{
			gettimeofday ( &tv, NULL );
			timeval2time_f ( &tv, timef );
			ret = serGetDevStatusLines ( dev, timef );
			if ( ret < 0 )
				return -1;
			if ( ret == 1 )
				return 0;

			usleep ( 1000 );
		}

		//timeout
		return -1;
		break;

#ifdef ENABLE_GPIO
	case SERPORT_MODE_GPIO:
		pollfds[0].fd = dev->fd;
		pollfds[0].events = POLLERR;

		i = poll(pollfds, 1, 10000); /* timeout 10 seconds */
		if (i != 1 && !(pollfds[0].revents & POLLERR) )
			return -1;

		gettimeofday ( &tv, NULL );
		timeval2time_f ( &tv, timef );

		if ( serGetDevStatusLines ( dev, timef ) < 0 )
			return -1;

		return 0;
		break;
#endif


#ifdef ENABLE_TIOCMIWAIT
	case SERPORT_MODE_IWAIT:
		signal ( SIGALRM, sigalrm );
		alarm ( 10 );

		if ( ioctl ( dev->fd, TIOCMIWAIT, dev->modemlines) != 0 )
			return -1;
		gettimeofday ( &tv, NULL );
		timeval2time_f ( &tv, timef );

		signal ( SIGALRM, SIG_DFL );
		alarm ( 0 );

		if ( serGetDevStatusLines ( dev, timef ) < 0 )
			return -1;

		return 0;
		break;
#endif

#ifdef ENABLE_TIMEPPS
	case SERPORT_MODE_TIMEPPS:
		timeout.tv_sec = 0;
		timeout.tv_nsec = 0;

		for ( i=0; i<10*100; i++ )
		{
			if ( time_pps_fetch ( dev->ppshandle, PPS_TSFMT_TSPEC, &ppsinfo, &timeout ) == -1 )
			{
				loggerf ( LOGGER_NOTE, "ppsfetch failed: %d\n", errno );
				return -1;
			}

			if ( ppsinfo.assert_sequence != dev->ppslastassert )
			{
				timespec2time_f ( &ppsinfo.assert_timestamp, timef );
				ppslines = TIOCM_CD;	//NOTE: assuming that pps support is on the DCD line
				dev->ppslastassert = ppsinfo.assert_sequence;

				if ( serStoreDevStatusLines ( dev, ppslines, timef ) < 0 )
					return -1;
				return 0;
			}
			else if ( ppsinfo.clear_sequence != dev->ppslastclear )
			{
				timespec2time_f ( &ppsinfo.clear_timestamp, timef );
				ppslines = 0;
				dev->ppslastclear = ppsinfo.clear_sequence;

				if ( serStoreDevStatusLines ( dev, ppslines, timef ) < 0 )
					return -1;
				return 0;
			}

			usleep ( 10000 );
		}

		return -1;
		break;
#endif

	}

	loggerf ( LOGGER_NOTE, "Error: serWaitForSerialChange(): mode not supported\n" );

	//unknown serial port mode !!
	return -1;

}
Example #6
0
/*@-mustfreefresh -type@ -unrecog*/
static /*@null@*/ void *gpsd_ppsmonitor(void *arg)
{
    struct gps_device_t *session = (struct gps_device_t *)arg;
    struct timeval  tv;
    struct timespec ts;
#if defined(TIOCMIWAIT)
    int cycle, duration, state = 0, laststate = -1, unchanged = 0;
    struct timeval pulse[2] = { {0, 0}, {0, 0} };
#endif /* TIOCMIWAIT */
#if defined(HAVE_SYS_TIMEPPS_H)
    int kpps_edge = 0;       /* 0 = clear edge, 1 = assert edge */
    int cycle_kpps, duration_kpps;
    struct timespec pulse_kpps[2] = { {0, 0}, {0, 0} };
    struct timespec tv_kpps;
    pps_info_t pi;
#endif
/* for chrony SOCK interface, which allows nSec timekeeping */
#define SOCK_MAGIC 0x534f434b
    struct sock_sample {
	struct timeval tv;
	double offset;
	int pulse;
	int leap;
	int _pad;	/* unused */
	int magic;      /* must be SOCK_MAGIC */
    } sample;
    /* chrony must be started first as chrony insists on creating the socket */
    /* open the chrony socket */
    int chronyfd = -1;
    char chrony_path[PATH_MAX];

    gpsd_report(LOG_PROG, "PPS Create Thread gpsd_ppsmonitor\n");

    /* wait for the device to go active - makes this safe to call early */
    while (session->gpsdata.gps_fd == -1) {
	/* should probably remove this once code is verified */
	gpsd_report(LOG_PROG, "PPS thread awaiting device activation\n");
	(void)sleep(1);
    }

    /*  Activates PPS support for RS-232 or USB devices only. */
    if (!(session->sourcetype == source_rs232 || session->sourcetype == source_usb)) {
	gpsd_report(LOG_PROG, "PPS thread deactivationde. Not RS-232 or USB device.\n");
	(void)ntpshm_free(session->context, session->shmTimeP);
	session->shmTimeP = -1;
	return NULL;
    }

    if ( 0 == getuid() ) {
	/* this case will fire on command-line devices; 
	 * they're opened before priv-dropping.  Matters because
         * only root can use /var/run.
	 */
	(void)snprintf(chrony_path, sizeof (chrony_path),
		"/var/run/chrony.%s.sock", basename(session->gpsdata.dev.path));
    } else {
	(void)snprintf(chrony_path, sizeof (chrony_path),
		"/tmp/chrony.%s.sock", 	basename(session->gpsdata.dev.path));
    }

    if (access(chrony_path, F_OK) != 0) {
	gpsd_report(LOG_PROG, "PPS chrony socket %s doesn't exist\n", chrony_path);
    } else {
	chronyfd = netlib_localsocket(chrony_path, SOCK_DGRAM);
	if (chronyfd < 0)
	    gpsd_report(LOG_PROG, "PPS can not connect chrony socket: %s\n",
		chrony_path);
	else
	    gpsd_report(LOG_RAW, "PPS using chrony socket: %s\n", chrony_path);
    }

    /* end chrony */

#if defined(HAVE_SYS_TIMEPPS_H)
    /* some operations in init_kernel_pps() require root privs */
    (void)init_kernel_pps( session );
    if ( 0 <= session->kernelpps_handle ) {
	gpsd_report(LOG_WARN, "KPPS kernel PPS will be used\n");
    }
    memset( (void *)&pi, 0, sizeof(pps_info_t));
#endif

    /* root privileges are not required after this point */

    /* 
     * Wait for status change on any handshake line. The only assumption here 
     * is that no GPS lights up more than one of these pins.  By waiting on
     * all of them we remove a configuration switch.
     */
    while (!gpsd_ppsmonitor_stop) {
	bool ok = false;
	char *log = NULL;
	char *log1 = NULL;

#if defined(TIOCMIWAIT)
#define PPS_LINE_TIOC (TIOCM_CD|TIOCM_CAR|TIOCM_RI|TIOCM_CTS)
        if (ioctl(session->gpsdata.gps_fd, TIOCMIWAIT, PPS_LINE_TIOC) != 0) {
	    gpsd_report(LOG_ERROR, "PPS ioctl(TIOCMIWAIT) failed: %d %.40s\n"
	    	, errno, strerror(errno));
	    break;
	}
#endif /* TIOCMIWAIT */

/*@-noeffect@*/
#ifdef HAVE_CLOCK_GETTIME
	/* using  clock_gettime() here, that is nSec, 
	 * not uSec like gettimeofday */
	if ( 0 > clock_gettime(CLOCK_REALTIME, &ts) ) {
	    /* uh, oh, can not get time! */
	    gpsd_report(LOG_ERROR, "PPS clock_gettime() failed\n");
	    break;
	}
	TSTOTV( &tv, &ts);
#else
	if ( 0 > gettimeofday(&tv, NULL) ) {
	    /* uh, oh, can not get time! */
	    gpsd_report(LOG_ERROR, "PPS gettimeofday() failed\n");
	    break;
	}
	TVTOTS( &ts, &tv);
#endif
/*@+noeffect@*/

#if defined(HAVE_SYS_TIMEPPS_H)
        if ( 0 <= session->kernelpps_handle ) {
	    struct timespec kernelpps_tv;
	    /* on a quad core 2.4GHz Xeon this removes about 20uS of 
	     * latency, and about +/-5uS of jitter over the other method */
            memset( (void *)&kernelpps_tv, 0, sizeof(kernelpps_tv));
	    if ( 0 > time_pps_fetch(session->kernelpps_handle, PPS_TSFMT_TSPEC
	        , &pi, &kernelpps_tv)) {
		gpsd_report(LOG_ERROR, "KPPS kernel PPS failed\n");
	    } else {
		// find the last edge
	    	if ( pi.assert_timestamp.tv_sec > pi.clear_timestamp.tv_sec ) {
		    kpps_edge = 1;
		    tv_kpps = pi.assert_timestamp;
	    	} else if ( pi.assert_timestamp.tv_sec < pi.clear_timestamp.tv_sec ) {
		    kpps_edge = 0;
		    tv_kpps = pi.clear_timestamp;
		} else if ( pi.assert_timestamp.tv_nsec > pi.clear_timestamp.tv_nsec ) {
		    kpps_edge = 1;
		    tv_kpps = pi.assert_timestamp;
		} else {
		    kpps_edge = 0;
		    tv_kpps = pi.clear_timestamp;
		}
		gpsd_report(LOG_PROG, "KPPS assert %ld.%09ld, sequence: %ld - "
		       "clear  %ld.%09ld, sequence: %ld\n",
		       pi.assert_timestamp.tv_sec,
		       pi.assert_timestamp.tv_nsec,
		       pi.assert_sequence,
		       pi.clear_timestamp.tv_sec,
		       pi.clear_timestamp.tv_nsec, 
		       pi.clear_sequence);
		gpsd_report(LOG_PROG, "KPPS data: using %s\n",
		       kpps_edge ? "assert" : "clear");

#define timediff_kpps(x, y)	(int)((x.tv_sec-y.tv_sec)*1000000+((x.tv_nsec-y.tv_nsec)/1000))
	        cycle_kpps = timediff_kpps(tv_kpps, pulse_kpps[kpps_edge]);
	        duration_kpps = timediff_kpps(tv_kpps, pulse_kpps[(int)(kpps_edge == 0)]);
		if ( 3000000 < duration_kpps ) {
		    // invisible pulse
		    duration_kpps = 0;
		}
#undef timediff_kpps
	        gpsd_report(LOG_INF, 
		    "KPPS cycle: %7d, duration: %7d @ %lu.%09lu\n",
		    cycle_kpps, duration_kpps,
		    (unsigned long)tv_kpps.tv_sec, 
		    (unsigned long)tv_kpps.tv_nsec);
		pulse_kpps[kpps_edge] = tv_kpps;
		ok = true;
		log = "KPPS";
	    }
	}
#endif /* HAVE_SYS_TIMEPPS_H */

#if defined(TIOCMIWAIT)
	ok = false;
	log = NULL;

	/*@ +ignoresigns */
	if (ioctl(session->gpsdata.gps_fd, TIOCMGET, &state) != 0) {
	    gpsd_report(LOG_ERROR, "PPS ioctl(TIOCMGET) failed\n");
	    break;
	}
	/*@ -ignoresigns */

	state = (int)((state & PPS_LINE_TIOC) != 0);
	/*@ +boolint @*/
#define timediff(x, y)	(int)((x.tv_sec-y.tv_sec)*1000000+x.tv_usec-y.tv_usec)
	cycle = timediff(tv, pulse[state]);
	duration = timediff(tv, pulse[(int)(state == 0)]);
#undef timediff
	/*@ -boolint @*/

	if (state == laststate) {
	    /* some pulses may be so short that state never changes */
	    if (999000 < cycle && 1001000 > cycle) {
		duration = 0;
		unchanged = 0;
		gpsd_report(LOG_RAW,
			    "PPS pps-detect on %s invisible pulse\n",
			    session->gpsdata.dev.path);
	    } else if (++unchanged == 10) {
		unchanged = 1;
		gpsd_report(LOG_WARN,
			    "PPS TIOCMIWAIT returns unchanged state, ppsmonitor sleeps 10\n");
		(void)sleep(10);
	    }
	} else {
	    gpsd_report(LOG_RAW, "PPS pps-detect on %s changed to %d\n",
			session->gpsdata.dev.path, state);
	    laststate = state;
	    unchanged = 0;
	}
	pulse[state] = tv;
	if (unchanged) {
	    // strange, try again
	    continue;
	}
	gpsd_report(LOG_INF, "PPS cycle: %7d, duration: %7d @ %lu.%06lu\n",
		    cycle, duration,
		    (unsigned long)tv.tv_sec, (unsigned long)tv.tv_usec);

	/*  only listen to PPS after 4 consecutive fixes, otherwise time
	 *  will be inaccurate.
	 *  Not sure yet how to handle uBlox UBX_MODE_TMONLY
	 *  Do not use ship_to_ntp here since it is synced to packets
	 *  and this thread is asynchonous to packets */
	if ( 3 < session->fixcnt ) {
	    /*
	     * The PPS pulse is normally a short pulse with a frequency of
	     * 1 Hz, and the UTC second is defined by the front edge. But we
	     * don't know the polarity of the pulse (different receivers
	     * emit different polarities). The duration variable is used to
	     * determine which way the pulse is going. The code assumes
	     * that the UTC second is changing when the signal has not
	     * been changing for at least 800ms, i.e. it assumes the duty
	     * cycle is at most 20%.
	     *
	     * Some GPSes instead output a square wave that is 0.5 Hz and each
	     * edge denotes the start of a second.
	     *
	     * Some GPSes, like the Globalsat MR-350P, output a 1uS pulse.
	     * The pulse is so short that TIOCMIWAIT sees a state change
	     * but by the time TIOCMGET is called the pulse is gone.
	     *
	     * A few stupid GPSes, like the Furuno GPSClock, output a 1.0 Hz
	     * square wave where the leading edge is the start of a second
	     *
	     * 5Hz GPS (Garmin 18-5Hz) pulses at 5Hz. Set the pulse length to
	     * 40ms which gives a 160ms pulse before going high.
	     *
	     */

	    log = "Unknown error";
	    if (199000 > cycle) {
		// too short to even be a 5Hz pulse
		log = "Too short for 5Hz\n";
	    } else if (201000 > cycle) {
		/* 5Hz cycle */
		/* looks like 5hz PPS pulse */
		if (100000 > duration) {
		    /* BUG: how does the code know to tell ntpd
		     * which 1/5 of a second to use?? */
		    ok = true;
		    log = "5Hz PPS pulse\n";
		}
	    } else if (999000 > cycle) {
		log = "Too long for 5Hz, too short for 1Hz\n";
	    } else if (1001000 > cycle) {
		/* looks like PPS pulse or square wave */
		if (0 == duration) {
		    ok = true;
		    log = "invisible pulse\n";
		} else if (499000 > duration) {
		    /* end of the short "half" of the cycle */
		    /* aka the trailing edge */
		    log = "1Hz trailing edge\n";
		} else if (501000 > duration) {
		    /* looks like 1.0 Hz square wave, ignore trailing edge */
		    if (state == 1) {
			ok = true;
			log = "square\n";
		    }
		} else {
		    /* end of the long "half" of the cycle */
		    /* aka the leading edge */
		    ok = true;
		    log = "1Hz leading edge\n";
		}
	    } else if (1999000 > cycle) {
		log = "Too long for 1Hz, too short for 2Hz\n";
	    } else if (2001000 > cycle) {
		/* looks like 0.5 Hz square wave */
		if (999000 > duration) {
		    log = "0.5 Hz square too short duration\n";
		} else if (1001000 > duration) {
		    ok = true;
		    log = "0.5 Hz square wave\n";
		} else {
		    log = "0.5 Hz square too long duration\n";
		}
	    } else {
		log = "Too long for 0.5Hz\n";
	    }
	} else {
	    /* not a good fix, but a test for an otherwise good PPS
	     * would go here */
	    log = "no fix.\n";
	}
#endif /* TIOCMIWAIT */

	if (ok) {
	    gpsd_report(LOG_RAW, "PPS edge accepted %.100s", log);
	    /* chrony expects tv-sec since Jan 1970 */
	    /* FIXME!! offset is double of the error from local time */
	    sample.pulse = 0;
	    sample.leap = session->context->leap_notify;
	    sample.magic = SOCK_MAGIC;
#if defined(HAVE_SYS_TIMEPPS_H)
            if ( 0 <= session->kernelpps_handle) {
		/* pick the right edge */
		if ( kpps_edge ) {
		    ts = pi.assert_timestamp; /* structure copy */
		} else {
		    ts = pi.clear_timestamp;  /* structure copy */
		}
		TSTOTV( &sample.tv, &ts);
	    } else
#endif
	    {
		sample.tv = tv; 	/* structure copy */
	    } 
	    /* FIXME!! this is wrong if signal is 5Hz or 10Hz instead of PPS */
	    /* careful, Unix time to nSec is more precision than a double */
	    sample.offset = 1 + session->last_fixtime - ts.tv_sec;
	    sample.offset -= ts.tv_nsec / 1e9;
/* was: defined(ONCORE_ENABLE) && defined(BINARY_ENABLE) */
#ifdef __UNUSED__
	    /*@-noeffect@*/
	    if (session->device_type == &oncore_binary) {
		int pulse_delay_ns = session->driver.oncore.pps_offset_ns;
	        sample.offset += (double)pulse_delay_ns / 1000000000;
	        ts.tv_nsec    -= pulse_delay_ns;
	        TS_NORM( &ts );
	    }
	    /*@+noeffect@*/
#endif

	    TSTOTV( &tv, &ts );
	    if (session->ship_to_ntpd) {
	        log1 = "accepted";
		if ( 0 <= chronyfd ) {
		    log1 = "accepted chrony sock";
		    (void)send(chronyfd, &sample, sizeof (sample), 0);
                }
		(void)ntpshm_pps(session, &tv);
	    } else {
	    	log1 = "skipped ship_to_ntp=0";
	    }
	    gpsd_report(LOG_RAW, 
		    "PPS edge %.20s %lu.%06lu offset %.9f\n",
		    log1,
		    (unsigned long)sample.tv.tv_sec,
		    (unsigned long)sample.tv.tv_usec,
		    sample.offset);
	    if (session->context->pps_hook != NULL)
		session->context->pps_hook(session, &tv);
	} else {
	    gpsd_report(LOG_RAW, "PPS edge rejected %.100s", log);
	}

    }
#if defined(HAVE_SYS_TIMEPPS_H)
    if (session->kernelpps_handle > 0) {
	gpsd_report(LOG_PROG, "PPS descriptor cleaned up\n");
	time_pps_destroy(session->kernelpps_handle);
    }
#endif
    if (chronyfd != -1)
	(void)close(chronyfd);
    gpsd_report(LOG_PROG, "PPS gpsd_ppsmonitor exited.\n");
    return NULL;
}
Example #7
0
/* wait for, and get, last two edges using RFC2783
 * return -1 for error
 *         0 for OK
 *         1 no edge found, continue
 *
 * on a quad core 2.4GHz Xeon using KPPS timestamp instead of plain
 * PPS timestamp removes about 20uS of latency, and about +/-5uS
 * of jitter
 */
static int get_edge_rfc2783(struct inner_context_t *inner_context,
                         struct timespec *prev_clock_ts,
                         int *prev_edge,
                         struct timespec *clock_ts,
                         int *edge,
                         volatile struct timedelta_t *last_fixtime)
{
    pps_info_t pi;
    char ts_str1[TIMESPEC_LEN], ts_str2[TIMESPEC_LEN];
    struct timespec kernelpps_tv;
    volatile struct pps_thread_t *thread_context = inner_context->pps_thread;

    if ( inner_context->pps_canwait ) {
	/*
	 * RFC2783 specifies that a NULL timeval means to wait, if
	 * PPS_CANWAIT is available.
	 *
	 * since we pps_canwait, we skipped the TIOMCIWAIT
	 *
	 * 3 second time out, some GPS output 0.5Hz and some RFC2783
	 * can only trigger on one edge
	 * a better and more complex solution would be to wait
	 * for 1/20 second and suffer the cycles
	 */
	kernelpps_tv.tv_sec = 3;
	kernelpps_tv.tv_nsec = 0;
    } else {
	/*
	 * We use of a non-NULL zero timespec here,
	 * which means to return immediately with -1 (section
	 * 3.4.3).  This is because we know we just got a pulse because
	 * TIOCMIWAIT just woke up.
	 * The timestamp has already been captured in the kernel, and we
	 * are merely fetching it here.
	 */
	memset( (void *)&kernelpps_tv, 0, sizeof(kernelpps_tv));
    }
    memset( (void *)&pi, 0, sizeof(pi)); /* paranoia */
    if ( 0 > time_pps_fetch(inner_context->kernelpps_handle, PPS_TSFMT_TSPEC
	, &pi, &kernelpps_tv)) {

	char errbuf[BUFSIZ] = "unknown error";
	(void)strerror_r(errno, errbuf, sizeof(errbuf));
	thread_context->log_hook(thread_context, THREAD_ERROR,
		    "KPPS:%s kernel PPS failed %s\n",
		    thread_context->devicename, errbuf);
	if ( ETIMEDOUT == errno || EINTR == errno ) {
		/* just a timeout */
		return 1;
	}
	return 0;
    }
    if ( inner_context->pps_canwait ) {
        /* get_edge_tiocmiwait() got this if !pps_canwait */

	/* quick, grab a copy of last fixtime before it changes */
	thread_lock(thread_context);
	*last_fixtime = thread_context->fix_in;
	thread_unlock(thread_context);
    }


    // find the last edge
    if ( pi.assert_timestamp.tv_sec > pi.clear_timestamp.tv_sec ) {
        /* assert 1 sec or more after than clear */
	*edge = 1;
    } else if ( pi.assert_timestamp.tv_sec < pi.clear_timestamp.tv_sec ) {
        /* assert 1 sec or more before than clear */
	*edge = 0;
    } else if ( pi.assert_timestamp.tv_nsec > pi.clear_timestamp.tv_nsec ) {
        /* assert less than 1 sec after clear */
	*edge = 1;
    } else {
        /* assert less than 1 sec before clear */
	*edge = 0;
    }
    if ( 1 == *edge ) {
        /* assert after clear */
	*prev_edge = 0;
	if ( 0 == pi.clear_timestamp.tv_sec ) {
                /* brain damaged pps-gpio sometimes never fills in clear
                 * so make it look like an invisible pulse
                 * if clear is the leading edge, then we are off by the
                 * pulse width */
		*prev_clock_ts = pi.assert_timestamp;
	} else {
		*prev_clock_ts = pi.clear_timestamp;
        }
	*clock_ts = pi.assert_timestamp;
    } else {
        /* assert before clear */
	*prev_edge = 1;
	*prev_clock_ts = pi.assert_timestamp;
	*clock_ts = pi.clear_timestamp;
    }
    /*
     * pps_seq_t is uint32_t on NetBSD, so cast to
     * unsigned long as a wider-or-equal type to
     * accomodate Linux's type.
     */
    timespec_str( &pi.assert_timestamp, ts_str1, sizeof(ts_str1) );
    timespec_str( &pi.clear_timestamp, ts_str2, sizeof(ts_str2) );
    thread_context->log_hook(thread_context, THREAD_PROG,
		"KPPS:%s assert %s, sequence: %lu, "
		"clear  %s, sequence: %lu - using: %.10s\n",
		thread_context->devicename,
		ts_str1,
		(unsigned long) pi.assert_sequence,
		ts_str2,
		(unsigned long) pi.clear_sequence,
		*edge ? "assert" : "clear");

    return 0;
}
Example #8
0
int
main(int argc, char **argv)
{
	int fd;
	FILE *fdo;
	pps_info_t pi;
	pps_params_t pp;
	pps_handle_t ph;
	int i, mode;
	u_int olda, oldc;
	struct timespec to;
	char const *ofn;

	ofn = NULL;
	while ((i = getopt(argc, argv, "aAbBcCeo:uv")) != -1) {
		switch (i) {
		case 'a': aflag = 1; break;
		case 'A': Aflag = 1; break;
		case 'b': aflag = 1; cflag = 1; break;
		case 'B': Aflag = 1; Cflag = 1; break;
		case 'c': cflag = 1; break;
		case 'C': Cflag = 1; break;
		case 'e': eflag = 1; break;
		case 'o': ofn = optarg; break;
		case 'u': uflag = 1; break;
		case 'v': vflag = 1; break;
		case '?':
		default:
			fprintf(stderr,
			    "Usage: ppsapitest [-aAcC] device\n");
			exit (1);
		}
	}
	if (ofn != NULL) {
		fdo = fopen(ofn, "w");
		if (fdo == NULL)
			err(1, "Cannot open %s", ofn);
	} else {
		fdo = NULL;
	}
	argc -= optind;
	argv += optind;
	if (argc > 0) {
		fd = open(argv[0], O_RDONLY);
		if (fd < 0) 
			err(1, argv[0]);
	} else {
		fd = 0;
	}
	i = time_pps_create(fd, &ph);
	if (i < 0)
		err(1, "time_pps_create");

	i = time_pps_getcap(ph, &mode);
	if (i < 0)
		err(1, "time_pps_getcap");
	if (vflag) {
		fprintf(stderr, "Supported modebits:");
		if (mode & PPS_CAPTUREASSERT)
			fprintf(stderr, " CAPTUREASSERT");
		if (mode & PPS_CAPTURECLEAR)
			fprintf(stderr, " CAPTURECLEAR");
		if (mode & PPS_OFFSETASSERT)
			fprintf(stderr, " OFFSETASSERT");
		if (mode & PPS_OFFSETCLEAR)
			fprintf(stderr, " OFFSETCLEAR");
		if (mode & PPS_ECHOASSERT)
			fprintf(stderr, " ECHOASSERT");
		if (mode & PPS_ECHOCLEAR)
			fprintf(stderr, " ECHOCLEAR");
		if (mode & PPS_CANWAIT)
			fprintf(stderr, " CANWAIT");
		if (mode & PPS_CANPOLL)
			fprintf(stderr, " CANPOLL");
		if (mode & PPS_TSFMT_TSPEC)
			fprintf(stderr, " TSPEC");
		if (mode & PPS_TSFMT_NTPFP)
			fprintf(stderr, " NTPFP");
		fprintf(stderr, "\n");
	}

	if (!aflag && !cflag) {
		if (mode & PPS_CAPTUREASSERT)
			aflag = 1;
		if (mode & PPS_CAPTURECLEAR)
			cflag = 1;
	}
	if (!Aflag && !Cflag) {
		Aflag = aflag;
		Cflag = cflag;
	}

	if (Cflag && !(mode & PPS_CAPTURECLEAR))
		errx(1, "-C but cannot capture on clear flank");

	if (Aflag && !(mode & PPS_CAPTUREASSERT))
		errx(1, "-A but cannot capture on assert flank");

	i = time_pps_getparams(ph, &pp);
	if (i < 0)
		err(1, "time_pps_getparams():");

	if (aflag)
		pp.mode |= PPS_CAPTUREASSERT;
	if (cflag)
		pp.mode |= PPS_CAPTURECLEAR;

	if (eflag & aflag)
		pp.mode |= PPS_ECHOASSERT;

	if (eflag & cflag)
		pp.mode |= PPS_ECHOCLEAR;

	if (!(pp.mode & PPS_TSFMT_TSPEC))
		pp.mode |= PPS_TSFMT_TSPEC;
	
	i = time_pps_setparams(ph, &pp);
	if (i < 0) {
		err(1, "time_pps_setparams(mode %x):", pp.mode);
	}

	/*
	 * Pick up first event outside the loop in order to not
	 * get something ancient into the outfile.
	 */
	to.tv_nsec = 0;
	to.tv_sec = 0;
	i = time_pps_fetch(ph, PPS_TSFMT_TSPEC, &pi, &to);
	if (i < 0)
		err(1, "time_pps_fetch()");
	olda = pi.assert_sequence;
	oldc = pi.clear_sequence;

	while (1) {
		to.tv_nsec = 0;
		to.tv_sec = 0;
		i = time_pps_fetch(ph, PPS_TSFMT_TSPEC, &pi, &to);
		if (i < 0)
			err(1, "time_pps_fetch()");
		if (oldc != pi.clear_sequence && Cflag)
			;
		else if (olda != pi.assert_sequence && Aflag)
			;
		else {
			usleep(10000);
			continue;
		}
		if (fdo != NULL) {
			if (fwrite(&pi, sizeof pi, 1, fdo) != 1)
				err(1, "Write error on %s", ofn);
			if (uflag)
				fflush(fdo);
		}
		Chew(&pi.assert_timestamp, &pi.clear_timestamp,
			pi.assert_sequence, pi.clear_sequence);
		olda = pi.assert_sequence;
		oldc = pi.clear_sequence;
	}
	return(0);
}