Esempio n. 1
0
/* offset is actual_ts - clock_ts */
static void chrony_send(struct gps_device_t *session, struct timedrift_t *td)
{
    struct sock_sample sample;

    /* chrony expects tv-sec since Jan 1970 */
    sample.pulse = 0;
    sample.leap = session->context->leap_notify;
    sample.magic = SOCK_MAGIC;
    /*@-type@*//* splint is confused about struct timespec */
    TSTOTV(&sample.tv, &td->clock);
    /*@-compdef@*/
    sample.offset = timespec_diff_ns(td->real, td->clock) / 1e9;
    /*@+compdef@*/
#ifdef __COVERITY__
    sample._pad = 0;
#endif /* __COVERITY__ */
    /*@+type@*/

    /*@-type@*/ /* splint is confused about struct timespec */
    gpsd_report(&session->context->errout, LOG_RAW,
		"PPS chrony_send %lu.%09lu @ %lu.%09lu Offset: %0.9f\n",
		(unsigned long)td->real.tv_sec,
		(unsigned long)td->real.tv_nsec,
		(unsigned long)td->clock.tv_sec,
		(unsigned long)td->clock.tv_nsec,
                sample.offset);
    /*@+type@*/
    (void)send(session->chronyfd, &sample, sizeof (sample), 0);
}
Esempio n. 2
0
/**
 * iodef_time_set_from_ntpstamp:
 * @time: Pointer to a #iodef_time_t object.
 * @buf: Pointer to a string containing an NTP timestamp.
 *
 * Fills the @time object with information provided within the @buf NTP timestamp.
 *
 * Returns: 0 on success, a negative value if an error occured.
 */
int iodef_time_set_from_ntpstamp(iodef_time_t *time, const char *buf)
{
        l_fp ts;
        struct timeval tv;
        unsigned ts_mask = TS_MASK;
        unsigned ts_roundbit = TS_ROUNDBIT;

        libiodef_return_val_if_fail(time, libiodef_error(LIBIODEF_ERROR_ASSERTION));
        libiodef_return_val_if_fail(buf, libiodef_error(LIBIODEF_ERROR_ASSERTION));

        if ( sscanf(buf, "%x.%x", &ts.l_ui, &ts.l_uf) < 2 )
                return -1;

        /*
         * This transformation is a reverse form of the one found in
         *  iodef_get_ntp_timestamp()
         */
        ts.l_ui -= JAN_1970;
        ts.l_uf -= ts_roundbit;
        ts.l_uf &= ts_mask;
        TSTOTV(&ts, &tv);

        time->sec = tv.tv_sec;
        time->usec = tv.tv_usec;
        time->gmt_offset = 0;

        return 0;
}
Esempio n. 3
0
void
test_MicrosecondsRounding(void) {
	const l_fp input = {{50}, 3865471UL}; /* Should round to 50.0009 */
	const struct timeval expected = {50, 900};
	struct timeval actual;

	TSTOTV(&input, &actual);

	TEST_ASSERT_EQUAL(expected.tv_sec, actual.tv_sec);
	TEST_ASSERT_EQUAL(expected.tv_usec, actual.tv_usec);
}
Esempio n. 4
0
void
test_Seconds(void) {
	const l_fp input = {{50}, 0}; /* 50.0 s */
	const struct timeval expected = {50, 0};
	struct timeval actual;

	TSTOTV(&input, &actual);

	TEST_ASSERT_EQUAL(expected.tv_sec, actual.tv_sec);
	TEST_ASSERT_EQUAL(expected.tv_usec, actual.tv_usec);
}
Esempio n. 5
0
void
test_MicrosecondsExact(void) {
	const u_long HALF = 2147483648UL;
	const l_fp input = {{50}, HALF}; /* 50.5 s */
	const struct timeval expected = {50, 500000};
	struct timeval actual;

	TSTOTV(&input, &actual);

	TEST_ASSERT_EQUAL(expected.tv_sec, actual.tv_sec);
	TEST_ASSERT_EQUAL(expected.tv_usec, actual.tv_usec);

}
Esempio n. 6
0
/* offset is actual_ts - clock_ts */
static void chrony_send(struct gps_device_t *session, struct timedelta_t *td)
{
    char real_str[TIMESPEC_LEN];
    char clock_str[TIMESPEC_LEN];
    struct timespec offset;
    struct sock_sample sample;
    struct tm tm;
    int leap_notify = session->context->leap_notify;

    /*
     * insist that leap seconds only happen in june and december
     * GPS emits leap pending for 3 months prior to insertion
     * NTP expects leap pending for only 1 month prior to insertion
     * Per http://bugs.ntp.org/1090
     *
     * ITU-R TF.460-6, Section 2.1, says lappe seconds can be primarily
     * in Jun/Dec but may be in March or September
     */
    (void)gmtime_r( &(td->real.tv_sec), &tm);
    if ( 5 != tm.tm_mon && 11 != tm.tm_mon ) {
        /* Not june, not December, no way */
        leap_notify = LEAP_NOWARNING;
    }


    /* chrony expects tv-sec since Jan 1970 */
    sample.pulse = 0;
    sample.leap = leap_notify;
    sample.magic = SOCK_MAGIC;
    /* chronyd wants a timeval, not a timspec, not to worry, it is
     * just the top of the second */
    TSTOTV(&sample.tv, &td->clock);
    /* calculate the offset as a timespec to not lose precision */
    TS_SUB( &offset, &td->real, &td->clock);
    /* if tv_sec greater than 2 then tv_nsec loses precision, but
     * not a big deal as slewing will be required */
    sample.offset = TSTONS( &offset );
    sample._pad = 0;

    timespec_str( &td->real, real_str, sizeof(real_str) );
    timespec_str( &td->clock, clock_str, sizeof(clock_str) );
    gpsd_log(&session->context->errout, LOG_RAW,
	     "PPS chrony_send %s @ %s Offset: %0.9f\n",
	     real_str, clock_str, sample.offset);
    (void)send(session->chronyfd, &sample, sizeof (sample), 0);
}
Esempio n. 7
0
void
test_OffsetCalculationPositiveOffset(void) {
	struct pkt rpkt;

	rpkt.precision = -16; // 0,000015259
	rpkt.rootdelay = HTONS_FP(DTOUFP(0.125));
	rpkt.rootdisp = HTONS_FP(DTOUFP(0.25));
	// Synch Distance: (0.125+0.25)/2.0 == 0.1875
	l_fp reftime;
	get_systime(&reftime);
	HTONL_FP(&reftime, &rpkt.reftime);

	l_fp tmp;

	// T1 - Originate timestamp
	tmp.l_ui = 1000000000UL;
	tmp.l_uf = 0UL;
	HTONL_FP(&tmp, &rpkt.org);

	// T2 - Receive timestamp
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 2147483648UL;
	HTONL_FP(&tmp, &rpkt.rec);

	// T3 - Transmit timestamp
	tmp.l_ui = 1000000002UL;
	tmp.l_uf = 0UL;
	HTONL_FP(&tmp, &rpkt.xmt);

	// T4 - Destination timestamp as standard timeval
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 0UL;
	struct timeval dst;
	TSTOTV(&tmp, &dst);
	dst.tv_sec -= JAN_1970;

	double offset, precision, synch_distance;
	offset_calculation(&rpkt, LEN_PKT_NOMAC, &dst, &offset, &precision, &synch_distance);

	TEST_ASSERT_EQUAL_DOUBLE(1.25, offset);
	TEST_ASSERT_EQUAL_DOUBLE(1. / ULOGTOD(16), precision);
	// 1.1250150000000001 ?
	TEST_ASSERT_EQUAL_DOUBLE(1.125015, synch_distance);
}
Esempio n. 8
0
void
test_OffsetCalculationNegativeOffset(void)
{
	struct pkt	rpkt;
	l_fp		reftime, tmp;
	struct timeval	dst;
	double		offset, precision, synch_distance;

	rpkt.precision = -1;
	rpkt.rootdelay = HTONS_FP(DTOUFP(0.5));
	rpkt.rootdisp = HTONS_FP(DTOUFP(0.5));
	
	/* Synch Distance is (0.5+0.5)/2.0, or 0.5 */
	get_systime(&reftime);
	HTONL_FP(&reftime, &rpkt.reftime);

	/* T1 - Originate timestamp */
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 0UL;
	HTONL_FP(&tmp, &rpkt.org);

	/* T2 - Receive timestamp */
	tmp.l_ui = 1000000000UL;
	tmp.l_uf = 2147483648UL;
	HTONL_FP(&tmp, &rpkt.rec);

	/*/ T3 - Transmit timestamp */
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 2147483648UL;
	HTONL_FP(&tmp, &rpkt.xmt);

	/* T4 - Destination timestamp as standard timeval */
	tmp.l_ui = 1000000003UL;
	tmp.l_uf = 0UL;

	TSTOTV(&tmp, &dst);
	dst.tv_sec -= JAN_1970;

	offset_calculation(&rpkt, LEN_PKT_NOMAC, &dst, &offset, &precision, &synch_distance);

	TEST_ASSERT_EQUAL_DOUBLE(-1, offset);
	TEST_ASSERT_EQUAL_DOUBLE(1. / ULOGTOD(1), precision);
	TEST_ASSERT_EQUAL_DOUBLE(1.3333483333333334, synch_distance);
}
Esempio n. 9
0
TEST_F(mainTest, OffsetCalculationNegativeOffset) {
	pkt rpkt;

	rpkt.precision = -1;
	rpkt.rootdelay = HTONS_FP(DTOUFP(0.5));
	rpkt.rootdisp = HTONS_FP(DTOUFP(0.5));
	// Synch Distance is (0.5+0.5)/2.0, or 0.5
	l_fp reftime;
	get_systime(&reftime);
	HTONL_FP(&reftime, &rpkt.reftime);

	l_fp tmp;

	// T1 - Originate timestamp
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 0UL;
	HTONL_FP(&tmp, &rpkt.org);

	// T2 - Receive timestamp
	tmp.l_ui = 1000000000UL;
	tmp.l_uf = 2147483648UL;
	HTONL_FP(&tmp, &rpkt.rec);

	// T3 - Transmit timestamp
	tmp.l_ui = 1000000001UL;
	tmp.l_uf = 2147483648UL;
	HTONL_FP(&tmp, &rpkt.xmt);

	// T4 - Destination timestamp as standard timeval
	tmp.l_ui = 1000000003UL;
	tmp.l_uf = 0UL;
	timeval dst;
	TSTOTV(&tmp, &dst);
	dst.tv_sec -= JAN_1970;

	double offset, precision, synch_distance;
	offset_calculation(&rpkt, LEN_PKT_NOMAC, &dst, &offset, &precision, &synch_distance);

	EXPECT_DOUBLE_EQ(-1, offset);
	EXPECT_DOUBLE_EQ(1. / ULOGTOD(1), precision);
	EXPECT_DOUBLE_EQ(1.3333483333333334, synch_distance);
}
Esempio n. 10
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;
}