Received: (from root@localhost)
	by whimsy.udel.edu (8.8.5/8.8.5) id GAA04270
	for stenn; Tue, 6 May 1997 06:21:27 GMT
Received: from ns.syseca-us.com by huey.udel.edu id aa09260; 3 May 97 22:13 EDT
Received: by loco.syseca-us.com id <19585>; Sat, 3 May 1997 19:20:29 -0700
Date: Sat, 3 May 1997 19:12:14 -0700
From: "Robert L. McMillin" <rlm@syseca-us.com>
To: mills@huey.udel.edu
Subject: Patches
MIME-Version: 1.0
Content-Type: Text/plain; charset=ISO-8859-1
Message-Id: <97May3.192029pdt.19585@loco.syseca-us.com>
Content-Transfer-Encoding: 8bit
X-MIME-Autoconverted: from quoted-printable to 8bit by whimsy.udel.edu id GAA04270


Here's the mods I wanted to send you, as patch diffs.  I didn't get to
the whole of the matter; clocktime.c looks pretty entrenched, and I
spent most of last night testing what I did modify.  In any event,
here's what's in this care package:

- Calendrical mods for include/ntp_calendar.h, lib/caljulian.c,
  lib/caltontp.c, and lib/calyearstart.c.  These pretty much gut
  the routines involved, and replace them with Ed Reingold's
  algorithms.
- Format 3 driver for the WWVB clock.  Since this has a different format
  length than the other two, it can be easily discerned.  However, I
  didn't try to force the use of the year from the clock; that would
  (potentially) involve rethinking the clocktime() interface, among
  other problems.  Notice that we get back a four-digit year from this
  format, which means that as long as we believe the clock, we should
  use its year, not the system's.
- Speaking of clocktime(): while I was looking at that module, I
  discovered that the constant GMT wasn't used everywhere it should have
  been.  I corrected this in two modules: refclock_chu.c, and
  refclock_leitch.c.
- Lastly, I show the code in ntp_refclock.c I had to dike out to make
  everything work with a straight-through cable to the RS485/RS232
  adaptor.

I have the hacked version running on our AlphaServer now, and it seems
to be cheerfully ticking away.  It should be interesting to see what
kind of statistics we get, because we seemed to be gaining about 5
min/month previously.

I hope most of these changes prove usable in the next release of xntpd,
unless you have some performance reasons for not using them.
calyearstart() now has two subroutine calls it didn't have before, but
again, I doubt seriously that any modern machine is that cycle-starved.

Happy chiming!

------------------------------------------------------------------------

*** xntp3-5.90/include/ntp_calendar.h.~1~	Mon May 30 02:48:49 1994
--- xntp3-5.90/include/ntp_calendar.h	Sat May  3 03:19:36 1997
***************
*** 1,6 ****
--- 1,8 ----
  /*
   * ntp_calendar.h - definitions for the calendar time-of-day routine
   */
+ #ifndef NTP_CALENDAR_H
+ #define NTP_CALENDAR_H
  
  #include "ntp_types.h"
  
***************
*** 75,80 ****
--- 77,112 ----
  #define	JANFEBNOLEAP	((JAN+FEB) * SECSPERDAY)
  #define	JANFEBLEAP	((JAN+FEBLEAP) * SECSPERDAY)
  
+ 
  extern	void	caljulian	P((u_long, struct calendar *));
  extern	u_long	caltontp	P((const struct calendar *));
  
+ /*
+  * Additional support stuff for Ed Rheingold's calendrical calculations
+  */
+ 
+ /*
+  * Start day of NTP time as days past the imaginary date 12/1/1 BC.
+  * (This is the beginning of the Christian Era, or BCE.)
+  */
+ #define DAY_NTP_STARTS 693596
+ /*
+  * The Gregorian calendar is based on a 400 year cycle.  This is the number
+  * of days in each cycle.
+  */
+ #define GREGORIAN_CYCLE_DAYS 146097
+ 
+ /*
+  * Days in a normal 100 year leap year calendar.  We lose a leap year day
+  * in years evenly divisible by 100 but not by 400.
+  */
+ #define GREGORIAN_NORMAL_CENTURY_DAYS 36524
+ 
+ /*
+  * Days in a normal 4 year leap year calendar cycle.
+  */
+ #define GREGORIAN_NORMAL_LEAP_CYCLE_DAYS 1461
+ 
+ #define is_leapyear(y) (y%4 == 0 && !(y%100 == 0 && !(y%400 == 0)))
+ 
+ #endif
*** xntp3-5.90/libntp/caljulian.c.~1~	Mon Mar 18 12:32:15 1996
--- xntp3-5.90/libntp/caljulian.c	Sat May  3 13:06:44 1997
***************
*** 8,105 ****
  #include "ntp_stdlib.h"
  
  /*
!  * calmonthtab - month start offsets from the beginning of a cycle.
   */
! static u_short calmonthtab[12] = {
! 	0,						/* March */
! 	MAR,						/* April */
! 	(MAR+APR),					/* May */
! 	(MAR+APR+MAY),					/* June */
! 	(MAR+APR+MAY+JUN),				/* July */
! 	(MAR+APR+MAY+JUN+JUL),				/* August */
! 	(MAR+APR+MAY+JUN+JUL+AUG),			/* September */
! 	(MAR+APR+MAY+JUN+JUL+AUG+SEP),			/* October */
! 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT),		/* November */
! 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV),		/* December */
! 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC),	/* January */
! 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN),	/* February */
! };
! 
! /*
!  * caldaytab - calendar year start day offsets
!  */
! static u_short caldaytab[YEARSPERCYCLE] = {
! 	(DAYSPERYEAR - (JAN + FEB)),
! 	((DAYSPERYEAR * 2) - (JAN + FEB)),
! 	((DAYSPERYEAR * 3) - (JAN + FEB)),
! 	((DAYSPERYEAR * 4) - (JAN + FEB)),
  };
  
  void
  caljulian(ntptime, jt)
! 	u_long ntptime;
! 	register struct calendar *jt;
  {
! 	register int i;
! 	register u_long nt;
! 	register u_short snt;
! 	register int cyear;
  
  	/*
! 	 * Find the start of the cycle this is in.
  	 */
! 	nt = ntptime;
! 	if (nt >= MAR1988) {
! 		cyear = CYCLE22;
! 		nt -= MAR1988;
! 	} else {
! 		cyear = 0;
! 		nt -= MAR1900;
! 	}
! 	while (nt >= SECSPERCYCLE) {
! 		nt -= SECSPERCYCLE;
! 		cyear++;
! 	}
! 	
  	/*
! 	 * Seconds, minutes and hours are too hard to do without
! 	 * divides, so we don't.
  	 */
! 	jt->second = (u_char) (nt % SECSPERMIN);
! 	nt /= SECSPERMIN;		/* nt in minutes */
! 	jt->minute = (u_char) (nt % MINSPERHR);
! 	snt = (u_short) (nt / MINSPERHR);		/* snt in hours */
! 	jt->hour = (u_char) (snt % HRSPERDAY);
! 	snt /= HRSPERDAY;		/* nt in days */
  
! 	/*
! 	 * snt is now the number of days into the cycle, from 0 to 1460.
! 	 */
! 	cyear <<= 2;
! 	if (snt < caldaytab[0]) {
! 		jt->yearday = snt + JAN + FEBLEAP + 1;	/* first year is leap */
! 	} else {
! 		for (i = 1; i < YEARSPERCYCLE; i++)
! 			if (snt < caldaytab[i])
! 				break;
! 		jt->yearday = snt - caldaytab[i-1] + 1;
! 		cyear += i;
! 	}
! 	jt->year = cyear + 1900;
  
! 	/*
! 	 * One last task, to compute the month and day.  Normalize snt to
! 	 * a day within a cycle year.
! 	 */
! 	while (snt >= DAYSPERYEAR)
! 		snt -= DAYSPERYEAR;
! 	for (i = 0; i < 11; i++)
! 		if (snt < calmonthtab[i+1])
! 			break;
  	
! 	if (i > 9)
! 		jt->month = i - 9;	/* January or February */
! 	else
! 		jt->month = i + 3;	/* March through December */
! 	jt->monthday = snt - calmonthtab[i] + 1;
  }
--- 8,114 ----
  #include "ntp_stdlib.h"
  
  /*
!  * calmonthtab - days-in-the-month table
   */
! static u_short calmonthtab[11] = {
!     JAN,
!     FEB,
!     MAR,
!     APR,
!     MAY,
!     JUN,
!     JUL,
!     AUG,
!     SEP,
!     OCT,
!     NOV
  };
  
  void
  caljulian(ntptime, jt)
!     u_long                    ntptime;
!     register struct calendar *jt;
  {
!     u_long ntp_day;
!     u_long minutes;
!     /*
!      * Absolute, zero-adjusted Christian era day, starting from the
!      * mythical day 12/1/1 BC
!      */
!     u_long acez_day;
! 
!     u_long d400;			     /* Days into a Gregorian cycle */
!     u_long d100;			     /* Days into a normal century */
!     u_long d4;				     /* Days into a 4-year cycle */
!     u_long n400;			     /* # of Gregorian cycles */
!     u_long n100;			     /* # of normal centuries */
!     u_long n4;				     /* # of 4-year cycles */
!     u_long n1;				     /* # of years into a leap year */
! 					     /*   cycle */
  
+     /*
+      * Do the easy stuff first: take care of hh:mm:ss, ignoring leap
+      * seconds
+      */
+     jt->second = ntptime % SECSPERMIN;
+     minutes    = ntptime / SECSPERMIN;
+     jt->minute = minutes % MINSPERHR;
+     jt->hour   = minutes % HRSPERDAY;
+     
+     /*
+      * Find the day past 1900/01/01 00:00 UTC
+      */
+     ntp_day = ntptime / SECSPERDAY;
+     acez_day = DAY_NTP_STARTS + ntp_day - 1;
+     n400     = acez_day/GREGORIAN_CYCLE_DAYS;
+     d400     = acez_day%GREGORIAN_CYCLE_DAYS;
+     n100     = d400 / GREGORIAN_NORMAL_CENTURY_DAYS;
+     d100     = d400 % GREGORIAN_NORMAL_CENTURY_DAYS;
+     n4       = d100 / GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
+     d4       = d100 % GREGORIAN_NORMAL_LEAP_CYCLE_DAYS;
+     n1       = d4 / DAYSPERYEAR;
+ 
+     /*
+      * Calculate the year and year-of-day
+      */
+     jt->yearday = 1 + d4%DAYSPERYEAR;
+     jt->year    = 400*n400 + 100*n100 + n4*4 + n1;
+ 
+     if (n100 == 4 || n1 == 4)
+     {
  	/*
! 	 * If the cycle year ever comes out to 4, it must be December 31st
! 	 * of a leap year.
  	 */
! 	jt->month    = 12;
! 	jt->monthday = 31;
! 	jt->yearday  = 366;
!     }
!     else
!     {
  	/*
! 	 * Else, search forwards through the months to get the right month
! 	 * and date.
  	 */
! 	int monthday;
  
! 	jt->year++;
! 	monthday = jt->yearday;
  
! 	for (jt->month=0;jt->month<11; jt->month++)
! 	{
! 	    int t;
  	
! 	    t = monthday - calmonthtab[jt->month];
! 	    if (jt->month == 1 && is_leapyear(jt->year))
! 		t--;
! 
! 	    if (t > 0)
! 		monthday = t;
! 	    else
! 		break;
! 	}
! 	jt->month++;
! 	jt->monthday = monthday;
!     }
  }
*** xntp3-5.90/libntp/caltontp.c.~1~	Mon May 30 02:49:47 1994
--- xntp3-5.90/libntp/caltontp.c	Sat May  3 15:41:43 1997
***************
*** 1,5 ****
  /*
!  * caltontp - convert a julian date to an NTP time
   */
  #include <sys/types.h>
  
--- 1,5 ----
  /*
!  * caltontp - convert a date to an NTP time
   */
  #include <sys/types.h>
  
***************
*** 7,90 ****
  #include "ntp_calendar.h"
  #include "ntp_stdlib.h"
  
- /*
-  * calmonthtab - month start offsets from the beginning of a cycle.
-  */
- static u_short calmonthtab[12] = {
- 	0,						/* March */
- 	MAR,						/* April */
- 	(MAR+APR),					/* May */
- 	(MAR+APR+MAY),					/* June */
- 	(MAR+APR+MAY+JUN),				/* July */
- 	(MAR+APR+MAY+JUN+JUL),				/* August */
- 	(MAR+APR+MAY+JUN+JUL+AUG),			/* September */
- 	(MAR+APR+MAY+JUN+JUL+AUG+SEP),			/* October */
- 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT),		/* November */
- 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV),		/* December */
- 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC),	/* January */
- 	(MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN),	/* February */
- };
- 
  u_long
! caltontp(jt)
! 	register const struct calendar *jt;
  {
! 	register int cyear;
! 	register int resyear;
! 	register u_long nt;
! 	register int yearday;
! 
! 	/*
! 	 * Find the start of the cycle this is in.
! 	 */
! 	cyear = (int)(jt->year - 1900) >> 2;
! 	resyear = (jt->year - 1900) - (cyear << 2);
! 	yearday = 0;
! 	if (resyear == 0) {
! 		if (jt->yearday == 0) {
! 			if (jt->month == 1 || jt->month == 2) {
! 				cyear--;
! 				resyear = 3;
! 			}
! 		} else {
! 			if (jt->yearday <= (u_short)(JAN+FEBLEAP)) {
! 				cyear--;
! 				resyear = 3;
! 				yearday = calmonthtab[10] + jt->yearday;
! 			} else {
! 				yearday = jt->yearday - (JAN+FEBLEAP);
! 			}
! 		}
! 	} else {
! 		if (jt->yearday == 0) {
! 			if (jt->month == 1 || jt->month == 2)
! 				resyear--;
! 		} else {
! 			if (jt->yearday <= (u_short)(JAN+FEB)) {
! 				resyear--;
! 				yearday = calmonthtab[10] + jt->yearday;
! 			} else {
! 				yearday = jt->yearday - (JAN+FEB);
! 			}
! 		}
! 	}
! 
! 	if (yearday == 0) {
! 		if (jt->month >= 3) {
! 			yearday = calmonthtab[jt->month - 3] + jt->monthday;
! 		} else {
! 			yearday = calmonthtab[jt->month + 9] + jt->monthday;
! 		}
! 	}
! 
! 	nt = TIMESDPERC((u_long)cyear);
! 	while (resyear-- > 0)
! 		nt += DAYSPERYEAR;
! 	nt += (u_long) (yearday - 1);
! 
! 	nt = TIMES24(nt) + (u_long)jt->hour;
! 	nt = TIMES60(nt) + (u_long)jt->minute;
! 	nt = TIMES60(nt) + (u_long)jt->second;
  
! 	return nt + MAR1900;
  }
--- 7,40 ----
  #include "ntp_calendar.h"
  #include "ntp_stdlib.h"
  
  u_long
! caltontp(register const struct calendar *jt)
  {
!     u_long ace_days;			     /* absolute Christian Era days */
!     u_long ntp_days;
!     int    prior_years;
!     u_long ntp_time;
!     
!     /*
!      * First convert today's date to absolute days past 12/1/1 BC
!      */
!     prior_years = jt->year-1;
!     ace_days = jt->yearday		     /* days this year */
! 	+(DAYSPERYEAR*prior_years)	     /* plus days in previous years */
! 	+(prior_years/4)		     /* plus prior years's leap days */
! 	-(prior_years/100)		     /* minus leapless century years */
! 	+(prior_years/400);		     /* plus leapful Gregorian yrs */
! 
!     /*
!      * Subtract out 1/1/1900, the beginning of the NTP epoch
!      */
!     ntp_days = ace_days - DAY_NTP_STARTS;
! 
!     /*
!      * Do the obvious:
!      */
!     ntp_time = 
! 	ntp_days*SECSPERDAY+SECSPERMIN*(MINSPERHR*jt->hour + jt->minute);
  
!     return ntp_time;
  }
*** xntp3-5.90/xntpd/refclock_chu.c.~1~	Tue Jan  7 22:17:11 1997
--- xntp3-5.90/xntpd/refclock_chu.c	Sat May  3 15:17:32 1997
***************
*** 483,489 ****
  	 * receive timestamp.  If this doesn't work, mark the
  	 * date as bad and forget it.
  	 */
! 	if (!clocktime(day, hour, minute, second, 0,
  	    rbufp->recv_time.l_ui, &pp->yearstart, (u_int32 *)&reftime)) {
  		refclock_report(peer, CEVNT_BADDATE);
  		return;
--- 483,489 ----
  	 * receive timestamp.  If this doesn't work, mark the
  	 * date as bad and forget it.
  	 */
! 	if (!clocktime(day, hour, minute, second, GMT,
  	    rbufp->recv_time.l_ui, &pp->yearstart, (u_int32 *)&reftime)) {
  		refclock_report(peer, CEVNT_BADDATE);
  		return;
*** xntp3-5.90/xntpd/ntp_refclock.c.~1~	Sat May  3 16:30:08 1997
--- xntp3-5.90/xntpd/ntp_refclock.c	Sat May  3 16:30:03 1997
***************
*** 861,868 ****
--- 861,870 ----
  		printf("refclock_open: fd %d modem status %lx\n",
  		    fd, ltemp);
  #endif
+ #if 0
  	if (ltemp & TIOCM_DSR)
  		ttyp->c_cflag &= ~CLOCAL;
+ #endif
  #endif /* TIOCMGET */
  	if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
  		msyslog(LOG_ERR,
*** xntp3-5.90/xntpd/refclock_leitch.c.~1~	Tue Dec 17 16:30:46 1996
--- xntp3-5.90/xntpd/refclock_leitch.c	Sat May  3 15:17:31 1997
***************
*** 478,484 ****
  		if (!leitch_get_time(rbufp,leitch,1)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, 0, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime1.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
--- 478,484 ----
  		if (!leitch_get_time(rbufp,leitch,1)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, GMT, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime1.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
***************
*** 495,501 ****
  		if (!leitch_get_time(rbufp,leitch,2)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, 0, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime2.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
--- 495,501 ----
  		if (!leitch_get_time(rbufp,leitch,2)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, GMT, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime2.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
***************
*** 512,518 ****
  		if (!leitch_get_time(rbufp,leitch,3)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, 0, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime3.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
--- 512,518 ----
  		if (!leitch_get_time(rbufp,leitch,3)) {
  		}
  		if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
! 			leitch->second, GMT, rbufp->recv_time.l_ui,
  			&leitch->yearstart, &leitch->reftime3.l_ui)) {
  			leitch->state = STATE_IDLE;
  			break;
*** xntp3-5.90/xntpd/refclock_wwvb.c.~1~	Tue May  7 22:09:26 1996
--- xntp3-5.90/xntpd/refclock_wwvb.c	Sat May  3 18:03:33 1997
***************
*** 11,21 ****
--- 11,23 ----
  #include <stdio.h>
  #include <ctype.h>
  #include <sys/time.h>
+ #include <time.h>
  
  #include "ntpd.h"
  #include "ntp_io.h"
  #include "ntp_refclock.h"
  #include "ntp_stdlib.h"
+ #include "ntp_calendar.h"
  
  /*
   * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
***************
*** 101,106 ****
--- 103,109 ----
  #define	NSAMPLES	3	/* stages of median filter */
  #define	LENWWVB0	22	/* format 0 timecode length */
  #define	LENWWVB2	24	/* format 2 timecode length */
+ #define LENWWVB3        29      /* format 3 timecode length */
  #define MONLIN		15	/* number of monitoring lines */
  
  /*
***************
*** 160,165 ****
--- 163,172 ----
  	int fd;
  	char device[20];
  
+ #ifdef DEBUG
+ 	if (debug)
+ 	    printf("inside wwvb_start\n");
+ #endif
  	/*
  	 * Open serial port. Use CLK line discipline, if available.
  	 */
***************
*** 169,175 ****
  #else
  	if (!(fd = refclock_open(device, SPEED232, 0)))
  #endif /* TTYCLK */
! 		return (0);
  
  	/*
  	 * Allocate and initialize unit structure
--- 176,188 ----
  #else
  	if (!(fd = refclock_open(device, SPEED232, 0)))
  #endif /* TTYCLK */
! 	{
! #ifdef DEBUG
! 	    if (debug)
! 		printf ("refclock_open barfed\n");
! #endif
! 	    return (0);
! 	}
  
  	/*
  	 * Allocate and initialize unit structure
***************
*** 238,243 ****
--- 251,260 ----
  	char	qualchar;	/* quality indicator */
  	char	leapchar;	/* leap indicator */
  
+ #ifdef DEBUG
+ 	if (debug)
+ 	    printf ("in wwvb_receive\n");
+ #endif
  	/*
  	 * Initialize pointers and read the timecode and timestamp
  	 */
***************
*** 307,312 ****
--- 324,359 ----
  		    &leapchar) == 9)
  			break;
  
+ 		case LENWWVB3:
+ 		    /*
+ 		     * Timecode format 3: "0003I yyyymmdd hhmmss+0000SL#"
+ 		     */
+ 		    {
+ 			int             month,day;
+ 			int             matched;
+ 	
+ 			qualchar = ' ';
+ 			matched =
+ 			    sscanf(pp->lastcode,"0003%c %4d%2d%2d %2d%2d%2d%*c%c",
+ 				   &syncchar,&pp->year,&month,&day,
+ 				   &pp->hour,&pp->minute,&pp->second,&leapchar);
+ 			if (matched==8)
+ 			{
+ 			    pp->msec = 0;
+ 			    pp->day = 31*(month - 1) + day;
+ 			    if (month > 2)
+ 			    {
+ 				pp->day = pp->day - (4*month + 23)/10;
+ 				if (is_leapyear(pp->year))
+ 				    pp->day++;
+ 			    }
+ 			    break;
+ 			}
+ #ifdef DEBUG
+ 			if (debug)
+ 			    printf ("wwvb_receive: Surrender, Dorothy!\n");
+ #endif
+ 		    }
  		default:
  
  		if (up->linect > 0)

-- 
Robert L. McMillin | Not the voice of Syseca, Inc. | rlm@syseca-us.com
	   Personal: rlm@helen.surfcty.com | rlm@netcom.com
