Permalink
Browse files

Imported sources.

  • Loading branch information...
0 parents commit fa008e7a26c8cf9acac8b61655250281a88b6a01 @richardcochran committed May 20, 2012
Showing with 496 additions and 0 deletions.
  1. +235 −0 leap.c
  2. +158 −0 lstab.c
  3. +49 −0 lstab.h
  4. +54 −0 makefile
235 leap.c
@@ -0,0 +1,235 @@
+/*
+ * leap.c
+ *
+ * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/timex.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "lstab.h"
+
+static void print_time_status(FILE *fp, int status)
+{
+ if (status & STA_PLL)
+ fprintf(fp, "\tSTA_PLL\tenable PLL updates (rw)\n");
+ if (status & STA_PPSFREQ)
+ fprintf(fp, "\tSTA_PPSFREQ\tenable PPS freq discipline (rw)\n");
+ if (status & STA_PPSTIME)
+ fprintf(fp, "\tSTA_PPSTIME\tenable PPS time discipline (rw)\n");
+ if (status & STA_FLL)
+ fprintf(fp, "\tSTA_FLL\tselect frequency-lock mode (rw)\n");
+ if (status & STA_INS)
+ fprintf(fp, "\tSTA_INS\tinsert leap (rw)\n");
+ if (status & STA_DEL)
+ fprintf(fp, "\tSTA_DEL\tdelete leap (rw)\n");
+ if (status & STA_UNSYNC)
+ fprintf(fp, "\tSTA_UNSYNC\tclock unsynchronized (rw)\n");
+ if (status & STA_FREQHOLD)
+ fprintf(fp, "\tSTA_FREQHOLD\thold frequency (rw)\n");
+ if (status & STA_PPSSIGNAL)
+ fprintf(fp, "\tSTA_PPSSIGNAL\tPPS signal present (ro)\n");
+ if (status & STA_PPSJITTER)
+ fprintf(fp, "\tSTA_PPSJITTER\tPPS signal jitter exceeded (ro)\n");
+ if (status & STA_PPSWANDER)
+ fprintf(fp, "\tSTA_PPSWANDER\tPPS signal wander exceeded (ro)\n");
+ if (status & STA_PPSERROR)
+ fprintf(fp, "\tSTA_PPSERROR\tPPS signal calibration error (ro)\n");
+ if (status & STA_CLOCKERR)
+ fprintf(fp, "\tSTA_CLOCKERR\tclock hardware fault (ro)\n");
+ if (status & STA_NANO)
+ fprintf(fp, "\tSTA_NANO\tresolution (0 = us, 1 = ns) (ro)\n");
+ if (status & STA_MODE)
+ fprintf(fp, "\tSTA_MODE\tmode (0 = PLL, 1 = FLL) (ro)\n");
+ if (status & STA_CLK)
+ fprintf(fp, "\tSTA_CLK\tclock source (0 = A, 1 = B) (ro)\n");
+}
+
+static int clear_status = 1;
+static int delay_usec = 1000000;
+static int insert_leap_second = 1;
+static int print_all = 0;
+static int set_date = 1;
+static int set_synchronized = 1;
+static int set_tai_offset = 1;
+static int verbose_status_bits = 0;
+
+static int leap_test(int index)
+{
+ struct timex tx;
+ struct timespec ts;
+ struct leap_second *ls = lstab_leap_second(index);
+ int tc;
+ FILE *fp = stdout;
+
+ if (clear_status) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_STATUS;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("ADJ_STATUS");
+ return -1;
+ }
+ sleep(1);
+ }
+ if (set_date) {
+ ts.tv_sec = ls->epoch.utc - 5;
+ ts.tv_nsec = 0;
+ if (clock_settime(CLOCK_REALTIME, &ts)) {
+ perror("clock_settime");
+ return -1;
+ }
+ }
+ if (set_synchronized) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_ESTERROR | ADJ_MAXERROR;
+ tx.esterror = 10;
+ tx.maxerror = 300000;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("ADJ_xxxERROR");
+ return -1;
+ }
+ }
+ if (clear_status) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_STATUS;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("ADJ_STATUS");
+ return -1;
+ }
+ }
+ if (set_tai_offset) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_TAI;
+ tx.constant = ls->leap.offset;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("ADJ_TAI");
+ return -1;
+ }
+ }
+ if (insert_leap_second) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_STATUS;
+ tx.status = STA_INS;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("STA_INS");
+ return -1;
+ }
+ }
+
+ ts.tv_sec = delay_usec / 1000000;
+ ts.tv_nsec = (delay_usec % 1000000) * 1000;
+
+ while (1) {
+ memset(&tx, 0, sizeof(tx));
+ tx.modes = ADJ_NANO;
+ tc = adjtimex(&tx);
+ if (tc < 0) {
+ perror("adjtimex");
+ return -1;
+ }
+ if (set_date && tx.time.tv_sec >= ls->epoch.utc + 5) {
+ break;
+ }
+ if (print_all || abs(tx.time.tv_sec - ls->epoch.utc) < 2) {
+ fprintf(fp, "%d %c%c %d %ld.%09ld\n",
+ tc,
+ tx.status & STA_INS ? 'I' : '-',
+ tx.status & STA_DEL ? 'D' : '-',
+ tx.tai,
+ tx.time.tv_sec, tx.time.tv_usec);
+ if (verbose_status_bits)
+ print_time_status(fp, tx.status);
+ }
+ if (delay_usec)
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &ts, NULL);
+ }
+
+ return 0;
+}
+
+static void usage(char *progname)
+{
+ fprintf(stderr,
+ "\nusage: %s [options]\n\n"
+ " -f [file] time table of leap seconds\n"
+ " -h prints this message and exits\n"
+ " -i leap second table index to use (default 1)\n"
+ " -p [num] print 'num' entries of the leap second table\n\n"
+ "leap test options:\n\n"
+ " -a print all calls to adjtimex (not just epoch +- 1)\n"
+ " -c do NOT clear all the status bits\n"
+ " -d do NOT set the historic date and time.\n"
+ " -l do NOT schedule a leap second insertion.\n"
+ " -s do NOT pretend to be synchronized.\n"
+ " -t do NOT set the historic TAI offset.\n"
+ " -u [num] usec delay between calls to adjtimex (default 1 second)\n"
+ " -v print the status bits verbosely\n"
+ "\n",
+ progname);
+}
+
+int main(int argc, char *argv[])
+{
+ char *timetable = NULL, *progname;
+ int c, index = 1, print_table = 0;
+
+ /* Process the command line arguments. */
+ progname = strrchr(argv[0], '/');
+ progname = progname ? 1+progname : argv[0];
+ while (EOF != (c = getopt(argc, argv, "fi:p:" "acdlstu:v" "h"))) {
+ switch (c) {
+ case 'f': timetable = optarg; break;
+ case 'i': index = atoi(optarg); break;
+ case 'p': print_table = atoi(optarg); break;
+ /**/
+ case 'a': print_all = 1; break;
+ case 'c': clear_status = 0; break;
+ case 'd': set_date = 0; break;
+ case 'l': insert_leap_second = 0; break;
+ case 's': set_synchronized = 0; break;
+ case 't': set_tai_offset = 0; break;
+ case 'u': delay_usec = atoi(optarg); break;
+ case 'v': verbose_status_bits = 1; break;
+ /**/
+ case 'h': usage(progname); return 0;
+ default: usage(progname); return -1;
+ }
+ }
+
+ lstab_init();
+
+ if (timetable && lstab_read(timetable))
+ return 1;
+
+ if (print_table)
+ lstab_print(stdout, print_table);
+
+ leap_test(index);
+
+ return 0;
+}
158 lstab.c
@@ -0,0 +1,158 @@
+/*
+ * lstab.c
+ *
+ * Copyright (C) 2012 Richard Cochran <richardcochran@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "lstab.h"
+
+/*
+ * We keep a history of the TAI - UTC offset in a lookup table.
+ *
+ * Each entry gives the NTP/TAI/UTC time when a new offset came into
+ * effect. This is always the second immediately after a leap second.
+ *
+ * The size of the table is the number of entries from the NIST table,
+ * plus room for two hundred more entries to be added at run time.
+ * Since there can be at most two leap seconds per year, this allows
+ * for at least one hundred years.
+ *
+ * The table data are available from
+ *
+ * ftp://time.nist.gov/pub/leap-seconds.list
+ *
+ * When updating this table, do not forget to set N_HISTORICAL_LEAPS.
+ */
+
+#define BASE_TAI_OFFSET 10
+#define N_HISTORICAL_LEAPS 26
+#define N_LEAPS (N_HISTORICAL_LEAPS + 200)
+#define NTP_UTC_OFFSET 2208988800UL
+#define NTP_TO_UTC(x) (x ## UL - 2208988800UL)
+
+static struct leap_second lstab[N_LEAPS];
+static int lstab_length;
+
+static uint32_t offset_table[N_LEAPS * 2] = {
+ 2272060800UL, 10, /* 1 Jan 1972 */
+ 2287785600UL, 11, /* 1 Jul 1972 */
+ 2303683200UL, 12, /* 1 Jan 1973 */
+ 2335219200UL, 13, /* 1 Jan 1974 */
+ 2366755200UL, 14, /* 1 Jan 1975 */
+ 2398291200UL, 15, /* 1 Jan 1976 */
+ 2429913600UL, 16, /* 1 Jan 1977 */
+ 2461449600UL, 17, /* 1 Jan 1978 */
+ 2492985600UL, 18, /* 1 Jan 1979 */
+ 2524521600UL, 19, /* 1 Jan 1980 */
+ 2571782400UL, 20, /* 1 Jul 1981 */
+ 2603318400UL, 21, /* 1 Jul 1982 */
+ 2634854400UL, 22, /* 1 Jul 1983 */
+ 2698012800UL, 23, /* 1 Jul 1985 */
+ 2776982400UL, 24, /* 1 Jan 1988 */
+ 2840140800UL, 25, /* 1 Jan 1990 */
+ 2871676800UL, 26, /* 1 Jan 1991 */
+ 2918937600UL, 27, /* 1 Jul 1992 */
+ 2950473600UL, 28, /* 1 Jul 1993 */
+ 2982009600UL, 29, /* 1 Jul 1994 */
+ 3029443200UL, 30, /* 1 Jan 1996 */
+ 3076704000UL, 31, /* 1 Jul 1997 */
+ 3124137600UL, 32, /* 1 Jan 1999 */
+ 3345062400UL, 33, /* 1 Jan 2006 */
+ 3439756800UL, 34, /* 1 Jan 2009 */
+ 3550089600UL, 35, /* 1 Jul 2012 */
+};
+
+static void leap_second_init(struct leap_second *ls, uint32_t val,
+ unsigned int offset, unsigned int prev_offset)
+{
+ /* the new epoch */
+
+ ls->epoch.ntp = val;
+ ls->epoch.utc = val - NTP_UTC_OFFSET;
+ ls->epoch.tai = val - NTP_UTC_OFFSET + offset;
+ ls->offset = offset;
+
+ /* the leap second before */
+
+ ls->leap.tai = val - NTP_UTC_OFFSET + offset - 1;
+ ls->leap.offset = prev_offset;
+}
+
+void lstab_init(void)
+{
+ int i;
+ struct leap_second *ls;
+ uint32_t val, offset, prev = 0;
+
+ for (i = 0; i < N_HISTORICAL_LEAPS; i++) {
+ ls = lstab + i;
+ val = offset_table[2*i];
+ offset = offset_table[2*i+1];
+ leap_second_init(ls, val, offset, prev);
+ prev = offset;
+ }
+ lstab_length = i;
+}
+
+struct leap_second *lstab_leap_second(unsigned int index)
+{
+ if (index < lstab_length)
+ return lstab + index;
+ return NULL;
+}
+
+void lstab_print(FILE *fp, int len)
+{
+ int i;
+ if (len > lstab_length) {
+ len = lstab_length;
+ }
+ fprintf(fp, "NTP\t\tOFF\tTAI\t\tUTC\n");
+ for (i = 0; i < len; i++) {
+ fprintf(fp, "%u\t%u\t%-10ld\t%-10ld\t%2d\n",
+ lstab[i].epoch.ntp, lstab[i].offset,
+ lstab[i].epoch.tai, lstab[i].epoch.utc, i);
+ }
+}
+
+int lstab_read(char *name)
+{
+ FILE *fp;
+ struct leap_second *ls;
+ uint32_t val, offset, prev = 0;
+ int index = 0;
+ char buf[1024];
+
+ fp = fopen(name, "r");
+ if (!fp)
+ return 1;
+
+ while (1) {
+ if (!fgets(buf, sizeof(buf), fp))
+ break;
+ if (2 == sscanf(buf, "%u %u", &val, &offset)) {
+ ls = lstab + index;
+ leap_second_init(ls, val, offset, prev);
+ prev = offset;
+ index++;
+ }
+ }
+ lstab_length = index;
+
+ return 0;
+}
Oops, something went wrong.

0 comments on commit fa008e7

Please sign in to comment.