FreeBSD/contrib/ntp/ntpd/refclock_mx4200.c
/*
 * Parse a mx4200 time recovery message. Returns a string if error.
 *
 * A typical message looks like this.  Checksum has already been stripped.
 *
 *    $PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL
 *
 *	Field	Field Contents
 *	-----	--------------
 *		Block Label: $PMVXG
 *		Sentence Type: 830=Time Recovery Results
 *			This sentence is output approximately 1 second
 *			preceding the 1PPS output.  It indicates the
 *			exact time of the next pulse, whether or not the
 *			time mark will be valid (based on operator-specified
 *			error tolerance), the time to which the pulse is
 *			synchronized, the receiver operating mode,
 *			and the time error of the *last* 1PPS output.
 *	1  char Time Mark Valid: T=Valid, F=Not Valid
 *	2  int  Year: 1993-
 *	3  int  Month of Year: 1-12
 *	4  int  Day of Month: 1-31
 *	5  int  Time of Day: HH:MM:SS
 *	6  char Time Synchronization: U=UTC, G=GPS
 *	7  char Time Recovery Mode: D=Dynamic, S=Static,
 *			K=Known Position, N=No Time Recovery
 *	8  int  Oscillator Offset: The filter's estimate of the oscillator
 *			frequency error, in parts per billion (ppb).
 *	9  int  Time Mark Error: The computed error of the *last* pulse
 *			output, in nanoseconds.
 *	10 int  User Time Bias: Operator specified bias, in nanoseconds
 *	11 int  Leap Second Flag: Indicates that a leap second will
 *			occur.  This value is usually zero, except during
 *			the week prior to the leap second occurrence, when
 *			this value will be set to +1 or -1.  A value of
 *			+1 indicates that GPS time will be 1 second
 *			further ahead of UTC time.
 *
 */
Parse a mx4200 time recovery message. Returns a string if error.
A typical message looks like this.  Checksum has already been stripped.

$PMVXG,830,T,YYYY,MM,DD,HH:MM:SS,U,S,FFFFFF,PPPPP,BBBBBB,LL

Field   Field Contents

Block Label: $PMVXG
Sentence Type: 830=Time Recovery Results
This sentence is output approximately 1 second
preceding the 1PPS output.  It indicates the
exact time of the next pulse, whether or not the
time mark will be valid (based on operator-specified
error tolerance), the time to which the pulse is
synchronized, the receiver operating mode,
and the time error of the *last* 1PPS output.
Parse a mx4200 time recovery message . Returns a string if error . A typical message looks like this . Checksum has already been stripped . $PMVXG 830 T YYYY MM DD HH : MM : SS U S FFFFFF PPPPP BBBBBB LL Field Field Contents Block Label : $PMVXG Sentence Type : 830 = Time Recovery Results This sentence is output approximately 1 second preceding the 1PPS output . It indicates the exact time of the next pulse whether or not the time mark will be valid ( based on operator - specified error tolerance ) the time to which the pulse is synchronized the receiver operating mode and the time error of the * last * 1PPS output .
static char *
mx4200_parse_t(
	struct peer *peer
	)
{
	struct refclockproc *pp;
	struct mx4200unit *up;
	char   time_mark_valid, time_sync, op_mode;
	int    sentence_type, valid;
	int    year, day_of_year, month, day_of_month;
	int    hour, minute, second, leapsec_warn;
	int    oscillator_offset, time_mark_error, time_bias;
	pp = peer->procptr;
	up = pp->unitptr;
	leapsec_warn = 0;  
	sscanf(pp->a_lastcode,
		"$PMVXG,%d,%c,%d,%d,%d,%d:%d:%d,%c,%c,%d,%d,%d,%d",
		&sentence_type, &time_mark_valid, &year, &month, &day_of_month,
		&hour, &minute, &second, &time_sync, &op_mode,
		&oscillator_offset, &time_mark_error, &time_bias, &leapsec_warn);
	if (sentence_type != PMVXG_D_TRECOVOUT)
		return ("wrong rec-type");
	switch (time_mark_valid) {
		case 'T':
			valid = 1;
			break;
		case 'F':
			valid = 0;
			break;
		default:
			return ("bad pulse-valid");
	}
	switch (time_sync) {
		case 'G':
			return ("synchronized to GPS; should be UTC");
		case 'U':
			break; 
		default:
			return ("not synchronized to UTC");
	}
	if (second > 60 || minute > 59 || hour > 23 ||
	    second <  0 || minute <  0 || hour <  0) {
		mx4200_debug(peer,
		    "mx4200_parse_t: bad time %02d:%02d:%02d",
		    hour, minute, second);
		if (leapsec_warn != 0)
			mx4200_debug(peer, " (leap %+d\n)", leapsec_warn);
		mx4200_debug(peer, "\n");
		refclock_report(peer, CEVNT_BADTIME);
		return ("bad time");
	}
	if ( second == 60 ) {
		msyslog(LOG_DEBUG,
		    "mx4200: leap second! %02d:%02d:%02d",
		    hour, minute, second);
	}
	if (day_of_month > 31 || month > 12 ||
	    day_of_month <  1 || month <  1 || year < YEAR_LAST_MODIFIED) {
		mx4200_debug(peer,
		    "mx4200_parse_t: bad date (%4d-%02d-%02d)\n",
		    year, month, day_of_month);
		refclock_report(peer, CEVNT_BADDATE);
		return ("bad date");
	}
	second--;
	if (second < 0) {
		second = 59;
		minute--;
		if (minute < 0) {
			minute = 59;
			hour--;
			if (hour < 0) {
				hour = 23;
				day_of_month--;
				if (day_of_month < 1) {
					return ("sorry, month boundary");
				}
			}
		}
	}
	if (!(day_of_year = mx4200_jday(year, month, day_of_month))) {
		mx4200_debug(peer,
		    "mx4200_parse_t: bad julian date %d (%4d-%02d-%02d)\n",
		    day_of_year, year, month, day_of_month);
		refclock_report(peer, CEVNT_BADDATE);
		return("invalid julian date");
	}
	switch (leapsec_warn) {
		case 0:
			pp->leap = LEAP_NOWARNING;
			break;
		case 1:
			pp->leap = LEAP_ADDSECOND;
			break;
		case -1:
			pp->leap = LEAP_DELSECOND;
			break;
		default:
			pp->leap = LEAP_NOTINSYNC;
	}
	if (leapsec_warn != up->last_leap ) {
		msyslog(LOG_DEBUG,
		    "mx4200: leap second warning: %d to %d (%d)",
		    up->last_leap, leapsec_warn, pp->leap);
	}
	up->last_leap = leapsec_warn;
	pp->year   = year;
	pp->day    = day_of_year;
	pp->hour   = hour;
	pp->minute = minute;
	pp->second = second;
	if (!valid || pp->leap == LEAP_NOTINSYNC) {
		mx4200_debug(peer, "mx4200_parse_t: time mark not valid\n");
		refclock_report(peer, CEVNT_BADTIME);
		return ("pulse invalid");
	}
	return (NULL);
}
static char * mx4200_parse_t ( struct peer * peer ) { struct refclockproc * pp ; struct mx4200unit * up ; char time_mark_valid , time_sync , op_mode ; int sentence_type , valid ; int year , day_of_year , month , day_of_month ; int hour , minute , second , leapsec_warn ; int oscillator_offset , time_mark_error , time_bias ; pp = peer -> procptr ; up = pp -> unitptr ; leapsec_warn = 0 ; sscanf ( pp -> a_lastcode , " " , & sentence_type , & time_mark_valid , & year , & month , & day_of_month , & hour , & minute , & second , & time_sync , & op_mode , & oscillator_offset , & time_mark_error , & time_bias , & leapsec_warn ) ; if ( sentence_type != PMVXG_D_TRECOVOUT ) return ( " " ) ; switch ( time_mark_valid ) { case ' ' : valid = 1 ; break ; case ' ' : valid = 0 ; break ; default : return ( " " ) ; } switch ( time_sync ) { case ' ' : return ( " " ) ; case ' ' : break ; default : return ( " " ) ; } if ( second > 60 || minute > 59 || hour > 23 || second < 0 || minute < 0 || hour < 0 ) { mx4200_debug ( peer , " " , hour , minute , second ) ; if ( leapsec_warn != 0 ) mx4200_debug ( peer , " \n " , leapsec_warn ) ; mx4200_debug ( peer , " \n " ) ; refclock_report ( peer , CEVNT_BADTIME ) ; return ( " " ) ; } if ( second == 60 ) { msyslog ( LOG_DEBUG , " " , hour , minute , second ) ; } if ( day_of_month > 31 || month > 12 || day_of_month < 1 || month < 1 || year < YEAR_LAST_MODIFIED ) { mx4200_debug ( peer , " \n " , year , month , day_of_month ) ; refclock_report ( peer , CEVNT_BADDATE ) ; return ( " " ) ; } second -- ; if ( second < 0 ) { second = 59 ; minute -- ; if ( minute < 0 ) { minute = 59 ; hour -- ; if ( hour < 0 ) { hour = 23 ; day_of_month -- ; if ( day_of_month < 1 ) { return ( " " ) ; } } } } if ( ! ( day_of_year = mx4200_jday ( year , month , day_of_month ) ) ) { mx4200_debug ( peer , " \n " , day_of_year , year , month , day_of_month ) ; refclock_report ( peer , CEVNT_BADDATE ) ; return ( " " ) ; } switch ( leapsec_warn ) { case 0 : pp -> leap = LEAP_NOWARNING ; break ; case 1 : pp -> leap = LEAP_ADDSECOND ; break ; case -1 : pp -> leap = LEAP_DELSECOND ; break ; default : pp -> leap = LEAP_NOTINSYNC ; } if ( leapsec_warn != up -> last_leap ) { msyslog ( LOG_DEBUG , " " , up -> last_leap , leapsec_warn , pp -> leap ) ; } up -> last_leap = leapsec_warn ; pp -> year = year ; pp -> day = day_of_year ; pp -> hour = hour ; pp -> minute = minute ; pp -> second = second ; if ( ! valid || pp -> leap == LEAP_NOTINSYNC ) { mx4200_debug ( peer , " \n " ) ; refclock_report ( peer , CEVNT_BADTIME ) ; return ( " " ) ; } return ( NULL ) ; }
Parse a mx4200 time recovery message.
Parse a mx4200 time recovery message .
/* Not all receivers output leap second warnings (!) */ /* UTC -> ok */ /*
	 * Check for insane time (allow for possible leap seconds)
	 */ /*
	 * Check for insane date
	 * (Certainly can't be any year before this code was last altered!)
	 */ /*
	 * Silly Hack for MX4200:
	 * ASCII message is for *next* 1PPS signal, but we have the
	 * timestamp for the *last* 1PPS signal.  So we have to subtract
	 * a second.  Discard if we are on a month boundary to avoid
	 * possible leap seconds and leap days.
	 */ /*
	 * Calculate Julian date
	 */ /*
	 * Setup leap second indicator
	 */ /*
	 * Any change to the leap second warning status?
	 */ /*
	 * Copy time data for billboard monitoring.
	 */ /*
	 * Toss if sentence is marked invalid
	 */