static int do_measure_overhead(const struct rtt_options* opts, struct rtt_endpoint* tx_ep, struct rtt_endpoint* rx_ep) { int n_iters = opts->n_iters; struct timespec a, b; int i; int* results; RTT_TEST( results = malloc(n_iters * sizeof(results[0])) ); /* NB. No need to do warm-ups here as we're only interested in the * median. */ for( i = 0; i < n_iters; ++i ) { clock_gettime(CLOCK_REALTIME, &a); tx_ep->ping(tx_ep); rx_ep->pong(rx_ep); clock_gettime(CLOCK_REALTIME, &b); results[i] = timespec_diff_ns(b, a); } int median; qsort(results, n_iters, sizeof(int), ci_qsort_compare_int); ci_iarray_median(results, results + n_iters, &median); free(results); return median; }
/* 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); }
static void do_pinger(const struct rtt_options* opts, struct rtt_endpoint* tx_ep, struct rtt_endpoint* rx_ep) { int overhead = measure_overhead(opts); int n_warm_ups = opts->n_warm_ups; int n_iters = opts->n_iters; int* results; int i; RTT_TEST( results = malloc(n_iters * sizeof(results[0])) ); for( i = 0; i < n_warm_ups; ++i ) { tx_ep->ping(tx_ep); rx_ep->pong(rx_ep); } if( tx_ep->reset_stats ) tx_ep->reset_stats(tx_ep); if( rx_ep->reset_stats ) rx_ep->reset_stats(rx_ep); /* Touch to ensure resident. */ memset(results, 0, n_iters * sizeof(results[0])); struct timespec start, end; for( i = 0; i < n_iters; ++i ) { clock_gettime(CLOCK_REALTIME, &start); tx_ep->ping(tx_ep); rx_ep->pong(rx_ep); clock_gettime(CLOCK_REALTIME, &end); results[i] = timespec_diff_ns(end, start) - overhead; if( opts->inter_iter_gap_ns ) { do clock_gettime(CLOCK_REALTIME, &start); while( timespec_diff_ns(start, end) < opts->inter_iter_gap_ns ); } } printf("# measurement_overhead: %d\n", overhead); if( tx_ep->dump_info != NULL ) tx_ep->dump_info(tx_ep, stdout); if( rx_ep != tx_ep && rx_ep->dump_info != NULL ) rx_ep->dump_info(rx_ep, stdout); for( i = 0; i < n_iters; ++i ) printf("%d\n", results[i]); }
static void do_warmup() { struct timespec t0, t; int i; get_current_time(&t0); do { for (i = 0; i < 100000; i++) ; get_current_time(&t); } while (timespec_diff_ns(&t0, &t) < (uint64_t)warmup * 1000000000); }
int main(int argc, char **argv) { unsigned int exp_universe, big, exp_n, n, n_1, i, j, m, repeats, alpha; struct priority_queue *pq, *pq1, *pq2; unsigned int *elems; unsigned int idx; struct timespec before, after; if (argc < 6) { printf("Usage : run_once exp_universe exp_n percentage filename repetitions big\n"); printf("Example : run_once 11 5 16 test_file 2 1 will run a test with:\n \t2^16 integers in the range [0,...2^11 - 1]\n\tOne of the structures will hold approximately 16 percent of the elements; the remaining amount will be in the other.\n\tThe test will be repeated (without any change in the input integers) 2 times.\n\tThe test is big so times will be measured in ms.\n"); exit(1); } big = atoi(argv[6]); exp_universe = atoi(argv[1]); exp_n = atoi(argv[2]); alpha = atoi(argv[3]); repeats = atoi(argv[5]); n = 1 << exp_n; n_1 = (alpha * n) / 100; elems = gen_instance(argv[4], n, exp_universe); for (m = 0; m < repeats; m++) { pq1 = pq_new(n_1, exp_universe); pq2 = pq_new(n - n_1, exp_universe); for (i = 0; i < n_1; i++) { pq_insert(pq1, elems[i]); } for (; i < n; i++) { pq_insert(pq2, elems[i]); } clock_gettime(CLOCK_MONOTONIC, &before); pq = pq_merge(pq1, pq2); clock_gettime(CLOCK_MONOTONIC, &after); if (big) { printf("merge: %lldms\n", timespec_diff_ms(after, before)); } else { printf("merge: %lldns\n", timespec_diff_ns(after, before)); } pq_free(pq); } free(elems); }
static uint64_t run_test_once(void *in, size_t size, TEEC_Operation *op, unsigned int l) { struct timespec t0, t1; TEEC_Result res; uint32_t ret_origin; if (random_in) read_random(in, size); get_current_time(&t0); res = TEEC_InvokeCommand(&sess, TA_SHA_PERF_CMD_PROCESS, op, &ret_origin); check_res(res, "TEEC_InvokeCommand"); get_current_time(&t1); return timespec_diff_ns(&t0, &t1); }
static void ubx_update(void) { unsigned char *buf; size_t data_len; unsigned short msgid; #ifdef PPS_ENABLE struct timedrift_t drift; #endif /* PPS_ENABLE */ buf = session.packet.outbuffer; msgid = (unsigned short)((buf[2] << 8) | buf[3]); data_len = (size_t) getles16(buf, 4); switch (msgid) { case UBX_NAV_SVINFO: display_nav_svinfo(&buf[6], data_len); break; case UBX_NAV_DOP: display_nav_dop(&buf[6], data_len); break; case UBX_NAV_SOL: display_nav_sol(&buf[6], data_len); break; default: break; } #ifdef PPS_ENABLE /*@-compdef@*/ /*@-type@*/ /* splint is confused about struct timespec */ if (pps_thread_lastpps(&session, &drift) > 0) { double timedelta = timespec_diff_ns(drift.real, drift.clock) * 1e-9; (void)mvwprintw(ppswin, 1, 13, "%.9f", timedelta); (void)wnoutrefresh(ppswin); } /*@+type@*/ /*@+compdef@*/ #endif /* PPS_ENABLE */ }
/*@-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; }
static void event_loop(struct server_state* ss) { msg(1, "Starting event loop\n"); struct timespec tx_ts, rx_ts, next_tx_ts, lost_tx_ts = { 0, 0 }; ss->have_sent = ss->have_tx_ts = ss->have_rx_ts = false; int rc, rx_left = ss->rx_msg_size; unsigned send_i = 0; clock_gettime(CLOCK_REALTIME, &next_tx_ts); timespec_add_ns(&next_tx_ts, ss->inter_tx_gap_ns); while( 1 ) { struct epoll_event e; TRY( rc = epoll_wait(ss->epoll, &e, 1, 0) ); if( rc == 0 ) { struct timespec now; clock_gettime(CLOCK_REALTIME, &now); if( ! timespec_le(next_tx_ts, now) ) continue; timespec_add_ns(&next_tx_ts, ss->inter_tx_gap_ns); if( ++send_i >= cfg_measure_nth && ! ss->have_sent ) { msg(3, "Send message (timed)\n"); TEST( send(ss->udp_sock_ts, ss->tx_buf_ts, ss->tx_msg_size, 0) == ss->tx_msg_size ); if( ! cfg_hw_ts ) { ss->have_tx_ts = true; tx_ts = now; } send_i = 0; ss->have_sent = true; } else { msg(3, "Send message\n"); TEST( send(ss->udp_sock, ss->tx_buf, ss->tx_msg_size, 0) == ss->tx_msg_size ); if( send_i >= cfg_measure_nth ) { /* Not had a reply to last timed message. Try to detect lost * messages. */ if( send_i == cfg_measure_nth ) { lost_tx_ts = now; } else if( ss->have_tx_ts && timespec_diff_ns(now, lost_tx_ts) > 10000000 ) { msg(2, "WARNING: No response to timed message\n"); if( ss->rtt_n > 0 ) ++(ss->n_lost_msgs); ss->have_sent = false; ss->have_tx_ts = false; ss->have_rx_ts = false; } } } } else if( e.data.fd == ss->tcp_sock ) { TEST( e.events & EPOLLIN ); if( cfg_hw_ts ) { rc = recv_ts(ss->tcp_sock, ss->rx_buf, rx_left, MSG_DONTWAIT, &rx_ts); } else { rc = recv(ss->tcp_sock, ss->rx_buf, rx_left, MSG_DONTWAIT); clock_gettime(CLOCK_REALTIME, &rx_ts); } if( rc > 0 ) { msg(3, "Received %d from client at %d.%09d\n", rc, (int) rx_ts.tv_sec, (int) rx_ts.tv_nsec); if( (rx_left -= rc) == 0 ) { send(ss->tcp_sock, ss->rx_buf, 1, MSG_NOSIGNAL); rx_left = ss->rx_msg_size; ss->have_rx_ts = true; if( ss->have_tx_ts ) measured_rtt(ss, tx_ts, rx_ts); } } else if( rc == 0 || errno == ECONNRESET ) { break; } else if( errno == ETIME ) { fprintf(stderr, "ERROR: Did not get H/W timestamp on RX\n"); exit(3); } else { TRY( rc ); } } else if( e.data.fd == ss->udp_sock_ts ) { assert( cfg_hw_ts ); assert( ! ss->have_tx_ts ); TEST( recv_ts(ss->udp_sock_ts, ss->rx_buf, 1, MSG_ERRQUEUE | MSG_DONTWAIT, &tx_ts) == 1 ); msg(3, "TX timestamp %d.%09d\n", (int) tx_ts.tv_sec, (int) tx_ts.tv_nsec); ss->have_tx_ts = true; if( ss->have_rx_ts ) measured_rtt(ss, tx_ts, rx_ts); } } msg(1, "Client disconnected\n"); TRY( close(ss->tcp_sock) ); }
/* gpsd_ppsmonitor() * * the core loop of the PPS thread. * All else is initialization, cleanup or subroutine */ static void *gpsd_ppsmonitor(void *arg) { char ts_str1[TIMESPEC_LEN], ts_str2[TIMESPEC_LEN]; struct inner_context_t inner_context = *((struct inner_context_t *)arg); volatile struct pps_thread_t *thread_context = inner_context.pps_thread; /* the GPS time and system clock timme, to the nSec, * when the last fix received * using a double would cause loss of precision */ volatile struct timedelta_t last_fixtime = {{0, 0}, {0, 0}}; struct timespec clock_ts = {0, 0}; time_t last_second_used = 0; long cycle = 0, duration = 0; /* state is the last state of the tty control signals */ int state = 0; /* count of how many cycles unchanged data */ int unchanged = 0; /* state_last is previous state */ int state_last = 0; /* edge, used as index into pulse to find previous edges */ int edge = 0; /* 0 = clear edge, 1 = assert edge */ #if defined(TIOCMIWAIT) int edge_tio = 0; long cycle_tio = 0; long duration_tio = 0; int state_tio = 0; int state_last_tio = 0; struct timespec clock_ts_tio = {0, 0}; /* pulse stores the time of the last two edges */ struct timespec pulse_tio[2] = { {0, 0}, {0, 0} }; #endif /* TIOCMIWAIT */ #if defined(HAVE_SYS_TIMEPPS_H) long cycle_kpps = 0, duration_kpps = 0; /* kpps_pulse stores the time of the last two edges */ struct timespec pulse_kpps[2] = { {0, 0}, {0, 0} }; #endif /* defined(HAVE_SYS_TIMEPPS_H) */ bool not_a_tty = false; /* before the loop, figure out how we can detect edges: * TIOMCIWAIT, which is linux specifix * RFC2783, a.k.a kernel PPS (KPPS) * or if KPPS is deficient a combination of the two */ if ( isatty(thread_context->devicefd) == 0 ) { thread_context->log_hook(thread_context, THREAD_INF, "KPPS:%s gps_fd:%d not a tty\n", thread_context->devicename, thread_context->devicefd); /* why do we care the device is a tty? so as not to ioctl(TIO..) * /dev/pps0 is not a tty and we need to use it */ not_a_tty = true; } /* if no TIOCMIWAIT, we hope to have PPS_CANWAIT */ if ( not_a_tty && !inner_context.pps_canwait ) { /* for now, no way to wait for an edge, in the future maybe figure out * a sleep */ } /* * this is the main loop, exit and never any further PPS processing. * * Four stages to the loop, * an unwanted condition at any point and the loop restarts * an error condition and we exit for all time. * * Stage One: wait for the next edge. * If we have KPPS * If we have PPS_CANWAIT * use KPPS and PPS_CANWAIT - this is the most accurate * else * use KPPS and TIOMCIWAIT together - this is pretty accurate * else If we have TIOMCIWAIT * use TIOMCIWAIT - this is the least accurate * else * give up * * Success is we have a good edge, otherwise loop some more * * On a successul stage one, we know this about the exact moment * of current pulse: * GPS (real) time * system (clock) time * edge type: Assert (rising) or Clear (falling) * * From the above 3 items, we can compute: * cycle length - elapsed time from the previous edge of the same type * pulse length (duration) - elapsed time from the previous edge * (the previous edge would be the opposite type) * * Stage Two: Categorize the current edge * Decide if we have 0.5Hz, 1Hz, 5 Hz cycle time * knowing cycle time determine if we have the leading or trailing edge * restart the loop if the edge looks dodgy * * Stage Three: Calculate * Calculate the offset (difference) between the system time * and the GPS time at the pulse moment * restart the loop if the offset looks dodgy * * Stage Four: Tell ntpd, chronyd, or gpsmon what we learned * a few more sanity checks * call the report hook with our PPS report */ while (thread_context->report_hook != NULL) { bool ok = false; char *log = NULL; char *edge_str = ""; if (++unchanged == 10) { /* last ten edges no good, stop spinning, just wait 10 seconds */ unchanged = 0; thread_context->log_hook(thread_context, THREAD_WARN, "PPS:%s unchanged state, ppsmonitor sleeps 10\n", thread_context->devicename); (void)sleep(10); } /* Stage One; wait for the next edge */ #if defined(TIOCMIWAIT) if ( !not_a_tty && !inner_context.pps_canwait ) { int ret; /* we are a tty, so can TIOCMIWAIT */ /* we have no PPS_CANWAIT, so must TIOCMIWAIT */ ret = get_edge_tiocmiwait( thread_context, &clock_ts_tio, &state_tio, &last_fixtime ); if ( 0 != ret ) { thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s die: TIOCMIWAIT Error\n", thread_context->devicename); break; } edge_tio = (state_tio > state_last_tio) ? 1 : 0; /* three things now known about the current edge: * clock_ts - time of the edge * state - the serial line input states * edge - rising edge (1), falling edge (0) or invisble edge (0) */ /* calculate cycle and duration from previous edges */ cycle_tio = timespec_diff_ns(clock_ts_tio, pulse_tio[edge_tio]); cycle_tio /= 1000; /* nsec to usec */ duration_tio = timespec_diff_ns(clock_ts_tio, pulse_tio[edge_tio ? 0 : 1])/1000; /* save this edge so we know next cycle time */ pulse_tio[edge_tio] = clock_ts_tio; /* use this data */ ok = true; clock_ts = clock_ts_tio; state = edge_tio; edge = edge_tio; edge_str = edge ? "Assert" : "Clear"; cycle = cycle_tio; duration = duration_tio; timespec_str( &clock_ts, ts_str1, sizeof(ts_str1) ); thread_context->log_hook(thread_context, THREAD_PROG, "TPPS:%s %.10s cycle: %d, duration: %d @ %s\n", thread_context->devicename, edge_str, cycle, duration, ts_str1); } #endif /* TIOCMIWAIT */ /* ok and log used by KPPS and TIOCMIWAIT */ log = NULL; #if defined(HAVE_SYS_TIMEPPS_H) if ( 0 <= inner_context.kernelpps_handle ) { int ret; int edge_kpps = 0; /* 0 = clear edge, 1 = assert edge */ /* time of the last edge */ struct timespec clock_ts_kpps = {0, 0}; /* time of the edge before the last edge */ struct timespec prev_clock_ts = {0, 0}; /* direction of next to last edge 1 = assert, 0 = clear */ int prev_edge = 0; /* get last and previsou edges, in order * optionally wait for goood data */ ret = get_edge_rfc2783(&inner_context, &prev_clock_ts, &prev_edge, &clock_ts_kpps, &edge_kpps, &last_fixtime); if ( -1 == ret ) { /* error, so break */ thread_context->log_hook(thread_context, THREAD_ERROR, "PPS:%s die: RFC2783 Error\n", thread_context->devicename); break; } if ( 1 == ret ) { /* no edge found, so continue */ /* maybe use TIOCMIWAIT edge instead?? */ continue; } /* for now, as we have been doing all of gpsd 3.x, just *use the last edge, not the previous edge */ /* compute time from previous saved similar edge */ cycle_kpps = timespec_diff_ns(clock_ts_kpps, pulse_kpps[edge_kpps]); cycle_kpps /= 1000; /* compute time from previous saved dis-similar edge */ duration_kpps = timespec_diff_ns(clock_ts_kpps, prev_clock_ts)/1000; /* save for later */ pulse_kpps[edge_kpps] = clock_ts_kpps; pulse_kpps[edge_kpps ? 0 : 1] = prev_clock_ts; /* sanity checks are later */ /* use this data */ state = edge_kpps; edge = edge_kpps; edge_str = edge ? "Assert" : "Clear"; clock_ts = clock_ts_kpps; cycle = cycle_kpps; duration = duration_kpps; timespec_str( &clock_ts_kpps, ts_str1, sizeof(ts_str1) ); thread_context->log_hook(thread_context, THREAD_PROG, "KPPS:%s %.10s cycle: %7d, duration: %7d @ %s\n", thread_context->devicename, edge_str, cycle_kpps, duration_kpps, ts_str1); } #endif /* defined(HAVE_SYS_TIMEPPS_H) */ if ( not_a_tty && !inner_context.pps_canwait ) { /* uh, oh, no TIOMCIWAIT, nor RFC2783, die */ thread_context->log_hook(thread_context, THREAD_WARN, "PPS:%s die: no TIOMCIWAIT, nor RFC2783 CANWAIT\n", thread_context->devicename); break; } /* * End of Stge One * we now know this about the exact moment of current pulse: * GPS (real) time * system (clock) time * edge type: Assert (rising) or Clear (falling) * * we have computed: * cycle length * pulse length (duration) */ /* * Stage Two: Categorize the current edge * Decide if we have 0.5Hz, 1Hz, 5 Hz cycle time * determine if we have the leading or trailing edge */ /* FIXME! this block duplicates a lot of the next block * of cycle detetion code */ if (state != state_last) { thread_context->log_hook(thread_context, THREAD_RAW, "PPS:%s %.10s pps-detect changed to %d\n", thread_context->devicename, edge_str, state); unchanged = 0; } else if ( (180000 < cycle && 220000 > cycle) /* 5Hz */ || (900000 < cycle && 1100000 > cycle) /* 1Hz */ || (1800000 < cycle && 2200000 > cycle) ) { /* 2Hz */ /* some pulses may be so short that state never changes * and some RFC2783 only can detect one edge */ duration = 0; unchanged = 0; thread_context->log_hook(thread_context, THREAD_RAW, "PPS:%s %.10s pps-detect invisible pulse\n", thread_context->devicename, edge_str); } /* else, unchannged state, and weird cycle time */ state_last = state; timespec_str( &clock_ts, ts_str1, sizeof(ts_str1) ); thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s %.10s cycle: %7d, duration: %7d @ %s\n", thread_context->devicename, edge_str, cycle, duration, ts_str1); 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. When the duration * is less than 1/2 the cycle we are on the trailing edge. * * 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. gpsd * calls that an invisible pulse. * * 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 * gpsd can only guess the correct edge. * * 5Hz GPS (Garmin 18-5Hz) pulses at 5Hz. Set the pulse length to * 40ms which gives a 160ms pulse before going high. * * You may think that PPS is very accurate, so the cycle time * valid window should be very small. This is not the case, * The Rasberry Pi clock is very coarse when it starts and chronyd * may be doing a fast slew. chronyd by default will slew up * to 8.334%! So the cycle time as measured by the system clock * may be almost +/- 9%. Therefore, gpsd uses a 10% window. * Don't worry, ntpd and chronyd will do further validation. */ 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 (900000 > cycle) { /* Yes, 10% window. The Rasberry Pi clock is very coarse * when it starts and chronyd may be doing a fast slew. * chronyd by default will slew up to 8.334% ! * Don't worry, ntpd and chronyd will do further sanitizing.*/ log = "Too long for 5Hz, too short for 1Hz\n"; } else if (1100000 > cycle) { /* Yes, 10% window. */ /* 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"; } /* end of Stage two * we now know what type of PPS pulse, and if we have the * leading edge */ /* Stage Three: Calculate * Calculate the offset (difference) between the system time * and the GPS time at the pulse moment */ /* * If there has not yet been any valid in-band time stashed * from the GPS when the PPS event was asserted, we can do * nothing further. gpsd can not tell what second this pulse is * in reference to. * * Some GPSes like Garmin always send a PPS, valid or not. * Other GPSes like some uBlox may only send PPS when time is valid. * It is common to get PPS, and no fixtime, while autobauding. */ if (last_fixtime.real.tv_sec == 0) { /* probably should log computed offset just for grins here */ ok = false; log = "missing last_fixtime\n"; } else if ( ok && last_second_used >= last_fixtime.real.tv_sec ) { /* uh, oh, this second already handled */ ok = false; log = "this second already handled\n"; } if ( !ok ) { /* can not use this pulse, reject and retry */ thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s %.10s rejected %.100s", thread_context->devicename, edge_str, log); continue; } /* we have validated a goood cycle, mark it */ unchanged = 0; /* offset is the skew from expected to observed pulse time */ struct timespec offset; /* offset as a printable string */ char offset_str[TIMESPEC_LEN]; /* delay after last fix */ struct timespec delay; /* delay as a printable string */ char delay_str[TIMESPEC_LEN]; char *log1 = ""; /* ppstimes.real is the time we think the pulse represents */ struct timedelta_t ppstimes; thread_context->log_hook(thread_context, THREAD_RAW, "PPS:%s %.10s categorized %.100s", thread_context->devicename, edge_str, log); /* FIXME! The GR-601W at 38,400 or faster can send the * serial fix before the interrupt event carrying the PPS * line assertion by about 10 mSec! */ /* * We get the time of the last fix recorded before the PPS came in, * which is for the previous cycle. Only works for integral cycle * times, but more than 1Hz is pointless. */ ppstimes.real.tv_sec = (time_t)last_fixtime.real.tv_sec + 1; ppstimes.real.tv_nsec = 0; /* need to be fixed for 5Hz */ ppstimes.clock = clock_ts; TS_SUB( &offset, &ppstimes.real, &ppstimes.clock); TS_SUB( &delay, &ppstimes.clock, &last_fixtime.clock); timespec_str( &delay, delay_str, sizeof(delay_str) ); /* end Stage Three: now known about the exact edge moment: * UTC time of PPS edge * offset of system time to PS time */ /* Stage Four: Tell ntpd, chronyd, or gpsmon what we learned * a few more sanity checks * call the report hook with our PPS report */ if ( 0> delay.tv_sec || 0 > delay.tv_nsec ) { thread_context->log_hook(thread_context, THREAD_RAW, "PPS:%s %.10s system clock went backwards: %.20s\n", thread_context->devicename, edge_str, delay_str); log1 = "system clock went backwards"; } else if ( ( 2 < delay.tv_sec) || ( 1 == delay.tv_sec && 100000000 > delay.tv_nsec ) ) { /* system clock could be slewing so allow 1.1 sec delay */ thread_context->log_hook(thread_context, THREAD_RAW, "PPS:%s %.10s no current GPS seconds: %.20s\n", thread_context->devicename, edge_str, delay_str); log1 = "timestamp out of range"; } else { last_second_used = last_fixtime.real.tv_sec; if (thread_context->report_hook != NULL) log1 = thread_context->report_hook(thread_context, &ppstimes); else log1 = "no report hook"; thread_lock(thread_context); thread_context->pps_out = ppstimes; thread_context->ppsout_count++; thread_unlock(thread_context); timespec_str( &ppstimes.clock, ts_str1, sizeof(ts_str1) ); timespec_str( &ppstimes.real, ts_str2, sizeof(ts_str2) ); thread_context->log_hook(thread_context, THREAD_INF, "PPS:%s %.10s hooks called clock: %s real: %s: %.20s\n", thread_context->devicename, edge_str, ts_str1, ts_str2, log1); } timespec_str( &clock_ts, ts_str1, sizeof(ts_str1) ); timespec_str( &offset, offset_str, sizeof(offset_str) ); thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s %.10s %.30s @ %s offset %.20s\n", thread_context->devicename, edge_str, log1, ts_str1, offset_str); /* end Stage four, end of the loop, do it again */ } #if defined(HAVE_SYS_TIMEPPS_H) if (inner_context.kernelpps_handle > 0) { thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s descriptor cleaned up\n", thread_context->devicename); (void)time_pps_destroy(inner_context.kernelpps_handle); } #endif thread_context->log_hook(thread_context, THREAD_PROG, "PPS:%s gpsd_ppsmonitor exited.\n", thread_context->devicename); return NULL; }
/*@ -globstate -nullpass (splint is confused) */ static void nmea_update(void) { char **fields; #ifdef PPS_ENABLE struct timedrift_t drift; #endif /* PPS_ENABLE */ assert(cookedwin != NULL); assert(nmeawin != NULL); assert(gpgsawin != NULL); assert(gpggawin != NULL); assert(gprmcwin != NULL); assert(gpgstwin != NULL); /* can be NULL if packet was overlong */ fields = session.driver.nmea.field; if (session.packet.outbuffer[0] == (unsigned char)'$' && fields != NULL && fields[0] != NULL) { int ymax, xmax; timestamp_t now; getmaxyx(nmeawin, ymax, xmax); assert(ymax > 0); if (strstr(sentences, fields[0]) == NULL) { char *s_end = sentences + strlen(sentences); if ((int)(strlen(sentences) + strlen(fields[0])) < xmax - 2) { *s_end++ = ' '; (void)strlcpy(s_end, fields[0], NMEA_MAX); } else { *--s_end = '.'; *--s_end = '.'; *--s_end = '.'; } (void)mvwaddstr(nmeawin, SENTENCELINE, 1, sentences); } /* * If the interval between this and last update is * the longest we've seen yet, boldify the corresponding * tag. */ now = timestamp(); if (now > last_tick && (now - last_tick) > tick_interval) { char *findme = strstr(sentences, fields[0]); tick_interval = now - last_tick; if (findme != NULL) { (void)mvwchgat(nmeawin, SENTENCELINE, 1, xmax - 13, A_NORMAL, 0, NULL); (void)mvwchgat(nmeawin, SENTENCELINE, 1 + (findme - sentences), (int)strlen(fields[0]), A_BOLD, 0, NULL); } } last_tick = now; if (strcmp(fields[0], "GPGSV") == 0 || strcmp(fields[0], "GNGSV") == 0 || strcmp(fields[0], "GLGSV") == 0) { int i; int nsats = (session.gpsdata.satellites_visible < MAXSATS) ? session.gpsdata.satellites_visible : MAXSATS; for (i = 0; i < nsats; i++) { (void)wmove(satwin, i + 2, 3); (void)wprintw(satwin, " %3d %3d%3d %3.0f", session.gpsdata.PRN[i], session.gpsdata.azimuth[i], session.gpsdata.elevation[i], session.gpsdata.ss[i]); } /* add overflow mark to the display */ if (nsats <= MAXSATS) (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_HLINE); else (void)mvwaddch(satwin, MAXSATS + 2, 18, ACS_DARROW); } if (strcmp(fields[0], "GPRMC") == 0 || strcmp(fields[0], "GNRMC") == 0 || strcmp(fields[0], "GLRMC") == 0) { /* time, lat, lon, course, speed */ (void)mvwaddstr(gprmcwin, 1, 12, fields[1]); (void)mvwprintw(gprmcwin, 2, 12, "%12s %s", fields[3], fields[4]); (void)mvwprintw(gprmcwin, 3, 12, "%12s %s", fields[5], fields[6]); (void)mvwaddstr(gprmcwin, 4, 12, fields[7]); (void)mvwaddstr(gprmcwin, 5, 12, fields[8]); /* the status field, FAA code, and magnetic variation */ (void)mvwaddstr(gprmcwin, 6, 12, fields[2]); (void)mvwaddstr(gprmcwin, 6, 25, fields[12]); (void)mvwprintw(gprmcwin, 7, 12, "%-5s%s", fields[10], fields[11]); cooked_pvt(); /* cooked version of PVT */ } if (strcmp(fields[0], "GPGSA") == 0 || strcmp(fields[0], "GNGSA") == 0 || strcmp(fields[0], "GLGSA") == 0) { char scr[128]; int i; (void)mvwprintw(gpgsawin, 1, 7, "%1s %s", fields[1], fields[2]); (void)wmove(gpgsawin, 2, 7); (void)wclrtoeol(gpgsawin); scr[0] = '\0'; for (i = 0; i < session.gpsdata.satellites_used; i++) { (void)snprintf(scr + strlen(scr), sizeof(scr) - strlen(scr), "%d ", session.gpsdata.used[i]); } getmaxyx(gpgsawin, ymax, xmax); (void)mvwaddnstr(gpgsawin, 2, 7, scr, xmax - 2 - 7); if (strlen(scr) >= (size_t) (xmax - 2)) { (void)mvwaddch(gpgsawin, 2, xmax - 2 - 7, (chtype) '.'); (void)mvwaddch(gpgsawin, 2, xmax - 3 - 7, (chtype) '.'); (void)mvwaddch(gpgsawin, 2, xmax - 4 - 7, (chtype) '.'); } monitor_fixframe(gpgsawin); (void)mvwprintw(gpgsawin, 3, 8, "%-5s", fields[16]); (void)mvwprintw(gpgsawin, 3, 16, "%-5s", fields[17]); (void)mvwprintw(gpgsawin, 3, 24, "%-5s", fields[15]); monitor_fixframe(gpgsawin); } if (strcmp(fields[0], "GPGGA") == 0 || strcmp(fields[0], "GNGGA") == 0 || strcmp(fields[0], "GLGGA") == 0) { (void)mvwprintw(gpggawin, 1, 12, "%-17s", fields[1]); (void)mvwprintw(gpggawin, 2, 12, "%-17s", fields[2]); (void)mvwprintw(gpggawin, 3, 12, "%-17s", fields[4]); (void)mvwprintw(gpggawin, 4, 12, "%-17s", fields[9]); (void)mvwprintw(gpggawin, 5, 12, "%1.1s", fields[6]); (void)mvwprintw(gpggawin, 5, 22, "%2.2s", fields[7]); (void)mvwprintw(gpggawin, 6, 12, "%-5.5s", fields[8]); (void)mvwprintw(gpggawin, 7, 12, "%-5.5s", fields[11]); } if (strcmp(fields[0], "GPGST") == 0) { (void)mvwprintw(gpgstwin, 1, 6, "%-10s", fields[1]); (void)mvwprintw(gpgstwin, 1, 21, "%-8s", fields[2]); (void)mvwprintw(gpgstwin, 2, 6, "%-10s", fields[3]); (void)mvwprintw(gpgstwin, 2, 21, "%-8s", fields[4]); (void)mvwprintw(gpgstwin, 3, 6, "%-10s", fields[5]); (void)mvwprintw(gpgstwin, 3, 21, "%-8s", fields[6]); (void)mvwprintw(gpgstwin, 4, 6, "%-10s", fields[7]); (void)mvwprintw(gpgstwin, 4, 21, "%-8s", fields[8]); } } #ifdef PPS_ENABLE /*@-compdef@*/ /*@-type@*/ /* splint is confused about struct timespec */ if (pps_thread_lastpps(&session, &drift) > 0) { double timedelta = timespec_diff_ns(drift.real, drift.clock) * 1e-9; (void)mvwprintw(gpgsawin, 4, 13, "%.9f", timedelta); (void)wnoutrefresh(gpgsawin); } /*@+type@*/ /*@+compdef@*/ #endif /* PPS_ENABLE */ }
void stop() { clock_gettime(CLOCK_REALTIME, &t1); timespec_diff_ns(t0, t1); }
int main(int argc, char **argv) { int option; int i; bool killall = false; bool verbose = false; int nsamples = INT_MAX; time_t timeout = (time_t)0, starttime = time(NULL); /* a copy of all old segments */ struct shm_stat_t shm_stat_old[NTPSEGMENTS + 1];; memset( shm_stat_old, 0 ,sizeof( shm_stat_old)); while ((option = getopt(argc, argv, "hn:st:vV")) != -1) { switch (option) { case 'n': nsamples = atoi(optarg); break; case 's': killall = true; break; case 't': timeout = (time_t)atoi(optarg); break; case 'v': verbose = true; break; case 'V': (void)fprintf(stderr, "%s: version %s (revision %s)\n", argv[0], VERSION, REVISION); exit(EXIT_SUCCESS); case 'h': fprintf(stderr, "usage: ntpshmmon [-s] [-n max] [-t timeout] [-v] [-h] [-V]\n" " -h print this help\n" " -n nsamples exit after nsamples\n" " -s remove SHMs and exit\n" " -t nseconds exit after nseconds\n" " -v be verbose\n" " -V print version and exit\n" ); exit(EXIT_SUCCESS); default: /* no option, just go and do it */ break; } } /* grab all segments, keep the non-null ones */ for (i = 0; i < NTPSEGMENTS; i++) { segments[i] = shm_get(i, false, true); if (verbose && segments[i] != NULL) fprintf(stderr, "unit %d opened\n", i); } if (killall) { struct shmTime **pp; for (pp = segments; pp < segments + NTPSEGMENTS; pp++) if (*pp != NULL) (void)shmdt((void *)(*pp)); exit(EXIT_SUCCESS); } /* * We want line buffering even if stdout is going to a file. This * is a (possibly futile) attempt to avoid writing an incomplete * line on interrupt. */ setvbuf(stdout, NULL, _IOLBF, 0); (void)printf("ntpshmmon version 1\n"); (void)printf("# Name Seen@ Clock Real L Prec\n"); do { /* the current segment */ struct shm_stat_t shm_stat; struct timespec delay; for (i = 0; i < NTPSEGMENTS; i++) { long long diff; /* 32 bit long is too short for a timespec */ enum segstat_t status = ntp_read(segments[i], &shm_stat, false); if (verbose) fprintf(stderr, "unit %d status %d\n", i, status); switch(status) { case OK: /* ntpd can slew the clock at 120% real time * so do not lock out slightly short cycles * use 50% of cycle time as lock out limit. * ignore that system time may jump. */ diff = timespec_diff_ns(shm_stat.tvr, shm_stat_old[i].tvr); if ( 0 == diff) { /* no change in tvr */ break; } diff = timespec_diff_ns(shm_stat.tvt, shm_stat_old[i].tvt); if ( 0 == diff) { /* no change in tvt */ break; } /* time stamp it */ clock_gettime(CLOCK_REALTIME, &shm_stat.tvc); printf("sample %s %ld.%09ld %ld.%09ld %ld.%09ld %d %3d\n", ntp_name(i), (long)shm_stat.tvc.tv_sec, shm_stat.tvc.tv_nsec, (long)shm_stat.tvr.tv_sec, shm_stat.tvr.tv_nsec, (long)shm_stat.tvt.tv_sec, shm_stat.tvt.tv_nsec, shm_stat.leap, shm_stat.precision); --nsamples; /* save the new time stamp */ shm_stat_old[i] = shm_stat; /* structure copy */ break; case NO_SEGMENT: break; case NOT_READY: /* do nothing, data not ready, wait another cycle */ break; case BAD_MODE: fprintf(stderr, "ntpshmmon: unknown mode %d on segment %s\n", shm_stat.status, ntp_name(i)); break; case CLASH: /* do nothing, data is corrupt, wait another cycle */ break; default: fprintf(stderr, "ntpshmmon: unknown status %d on segment %s\n", status, ntp_name(i)); break; } } /* all segments now checked */ /* * Even on a 1 Hz PPS, a sleep(1) may end up * being sleep(1.1) and missing a beat. Since * we're ignoring duplicates via timestamp, polling * at fast intervals should not be a problem * * PPS is not always one pulse per second. * the Garmin GPS 18x-5Hz outputs 5 pulses per second. * That is a 200 millSec cycle, minimum 20 milliSec duration * we will wait 1 milliSec out of caution * * and, of course, nanosleep() may sleep a lot longer than we ask... */ if ( timeout ) { /* do not read time unless it matters */ if ( time(NULL) > (starttime + timeout ) ) { /* time to exit */ break; } } /* wait 1,000 uSec */ delay.tv_sec = 0; delay.tv_nsec = 1000000L; nanosleep(&delay, NULL); } while ( 0 < nsamples ); exit(EXIT_SUCCESS); }