NetBSD/usr.sbin/xntp/xntpd/refclock_parse.c

4372 lines
114 KiB
C

/* $NetBSD: refclock_parse.c,v 1.4 1998/04/01 15:01:24 christos Exp $ */
/*
* /src/NTP/REPOSITORY/v4/xntpd/refclock_parse.c,v 3.103 1997/07/12 15:35:16 kardel Exp
*
* refclock_parse.c,v 3.103 1997/07/12 15:35:16 kardel Exp
*
* generic reference clock driver for receivers
*
* make use of a STREAMS module for input processing where
* available and configured. Currently the STREAMS module
* is only available for Suns running SunOS 4.x and SunOS5.x
*
* Copyright (c) 1989, 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997 by Frank Kardel
* Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
*
* 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.
*
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined(REFCLOCK) && defined(PARSE)
/*
* Defines:
* REFCLOCK && (PARSE||PARSEPPS)
* - enable this mess
* STREAM - allow for STREAMS modules
* ("parse", "ppsclocd", "ppsclock")
* PPS - use ppsclock module ioctl
*
* TTY defines:
* HAVE_BSD_TTYS - currently unsupported
* HAVE_SYSV_TTYS - will use termio.h
* HAVE_TERMIOS - will use termios.h
* STREAM - will use streams and implies HAVE_TERMIOS
*/
/*
* This driver currently provides the support for
* - Meinberg DCF77 receiver DCF77 PZF 535 (TCXO version) (DCF)
* - Meinberg DCF77 receiver DCF77 PZF 535 (OCXO version) (DCF)
* - Meinberg DCF77 receiver DCF77 AM receivers (DCF)
* - IGEL CLOCK (DCF)
* - ELV DCF7000 (DCF)
* - Schmid clock (DCF)
* - Conrad DCF77 receiver module (DCF)
* - FAU DCF77 NTP receiver (TimeBrick) (DCF)
*
* - Meinberg GPS166 (GPS)
* - Trimble SV6 (TSIP and TAIP protocol) (GPS)
*
* - RCC8000 MSF Receiver (MSF)
*/
/*
* Meinberg receivers are connected via a 9600 baud serial line
*
* Receivers that do NOT support:
* - leap second indication
* DCF U/A 31
* DCF PZF535 (stock version)
*
* CORRECTION: Meinberg will include LEAP SECOND anounncements in the
* telegram. This might not be true for all of their products, but
* some of them will. So you should definitely ask for NTP and
* LEAP SECOND announcements when ordering receivers from them.
*
* so...
* - for PZF535 please ask for revision PZFUERL4.6 or higher
* (support for leap second and alternate antenna)
*
* - LEAP SECOND announcement / NTP support for the other
* receivers
*
* The Meinberg GPS receiver also has a special NTP time stamp
* format. The firmware release is Uni-Erlangen. Only this
* firmware release is supported by xntp3.
*
* Meinberg generic receiver setup:
* output time code every second
* Baud rate 9600 7E2S
*/
#include "ntpd.h"
#include "ntp_refclock.h"
#include "ntp_unixtime.h" /* includes <sys/time.h> */
#include "ntp_control.h"
#include <stdio.h>
#include <ctype.h>
#ifndef TM_IN_SYS_TIME
# include <time.h>
#endif
#include <sys/errno.h>
extern int errno;
#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
# include "Bletch: Define one of {STREAM,HAVE_SYSV_TTYS,HAVE_TERMIOS}"
#endif
#ifdef STREAM
# include <sys/stream.h>
# include <sys/stropts.h>
# ifndef HAVE_TERMIOS
# define HAVE_TERMIOS
# endif
#endif
#ifdef HAVE_TERMIOS
# include <termios.h>
# define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
# define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
# undef HAVE_SYSV_TTYS
#endif
#ifdef HAVE_SYSV_TTYS
# include <termio.h>
# define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
# define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
#endif
#ifdef HAVE_BSD_TTYS
/* #error CURRENTLY NO BSD TTY SUPPORT */
# include "Bletch: BSD TTY not currently supported"
#endif
#if !defined(O_RDWR) /* XXX SOLARIS */
# include <fcntl.h>
#endif /* !def(O_RDWR) */
#ifdef PPS
# include <sys/ppsclock.h>
#endif
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include "ntp_io.h"
#include "ntp_select.h"
#include "ntp_stdlib.h"
#include "parse.h"
#if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__)
static char rcsid[]="refclock_parse.c,v 3.103 1997/07/12 15:35:16 kardel Exp";
#endif
/**===========================================================================
** external interface to xntp mechanism
**/
static void parse_init P((void));
static int parse_start P((int, struct peer *));
static void parse_shutdown P((int, struct peer *));
static void parse_poll P((int, struct peer *));
static void parse_control P((int, struct refclockstat *, struct refclockstat *));
#define parse_buginfo noentry
struct refclock refclock_parse = {
parse_start,
parse_shutdown,
parse_poll,
parse_control,
parse_init,
parse_buginfo,
NOFLAGS
};
/*
* the unit field selects for one the prototype to be used (lower 4 bits)
* and for the other the clock type in case of different but similar
* receivers (bits 4-6)
* the most significant bit encodes PPS support
* when the most significant bit is set the pps telegrams will be used
* for controlling the local clock (ntp_loopfilter.c)
* receiver specific configration data is kept in the parse_clockinfo field.
*/
/*
* Definitions
*/
#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
/**===========================================================================
** function vector for dynamically binding io handling mechanism
**/
struct parseunit;
typedef struct bind
{
char *bd_description; /* name of type of binding */
int (*bd_init) /* initialize */
P((struct parseunit *));
void (*bd_end) /* end */
P((struct parseunit *));
int (*bd_setcs) /* set character size */
P((struct parseunit *, parsectl_t *));
int (*bd_disable) /* disable */
P((struct parseunit *));
int (*bd_enable) /* enable */
P((struct parseunit *));
int (*bd_getfmt) /* get format */
P((struct parseunit *, parsectl_t *));
int (*bd_setfmt) /* setfmt */
P((struct parseunit *, parsectl_t *));
int (*bd_timecode) /* get time code */
P((struct parseunit *, parsectl_t *));
void (*bd_receive) /* receive operation */
P((struct recvbuf *));
void (*bd_poll) /* poll operation */
P((struct parseunit *));
} bind_t;
#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
#define PARSE_POLL(_X_) (*(_X_)->binding->bd_poll)(_X_)
/*
* io modes
*/
#define PARSE_F_NOPOLLONLY 0x0001 /* always do async io (possible PPS support via PARSE) */
#define PARSE_F_POLLONLY 0x0002 /* never do async io (no PPS support via PARSE) */
#define PARSE_F_PPSPPS 0x0004 /* use loopfilter PPS code (CIOGETEV) */
#define PARSE_F_PPSONSECOND 0x0008 /* PPS pulses are on second */
/**===========================================================================
** error message regression handling
**
** there are quite a few errors that can occur in rapid succession such as
** noisy input data or no data at all. in order to reduce the amount of
** syslog messages in such case, we are using a backoff algorithm. We limit
** the number of error messages of a certain class to 1 per time unit. if a
** configurable number of messages is displayed that way, we move on to the
** next time unit / count for that class. a count of messages that have been
** suppressed is held and displayed whenever a corresponding message is
** displayed. the time units for a message class will also be displayed.
** whenever an error condition clears we reset the error message state,
** thus we would still generate much output on pathological conditions
** where the system oscillates between OK and NOT OK states. coping
** with that condition is currently considered too complicated.
**/
#define ERR_ALL ~0 /* "all" errors */
#define ERR_BADDATA 0 /* unusable input data/conversion errors */
#define ERR_NODATA 1 /* no input data */
#define ERR_BADIO 2 /* read/write/select errors */
#define ERR_BADSTATUS 3 /* unsync states */
#define ERR_BADEVENT 4 /* non nominal events */
#define ERR_INTERNAL 5 /* internal error */
#define ERR_CNT (ERR_INTERNAL+1)
#define ERR(_X_) if (list_err(parse, (_X_)))
struct errorregression
{
u_long err_count; /* number of repititions per class */
u_long err_delay; /* minimum delay between messages */
};
static struct errorregression
err_baddata[] = /* error messages for bad input data */
{
{ 1, 0 }, /* output first message immediately */
{ 5, 60 }, /* output next five messages in 60 second intervals */
{ 3, 3600 }, /* output next 3 messages in hour intervals */
{ 0, 12*3600 } /* repeat messages only every 12 hours */
};
static struct errorregression
err_nodata[] = /* error messages for missing input data */
{
{ 1, 0 }, /* output first message immediately */
{ 5, 60 }, /* output next five messages in 60 second intervals */
{ 3, 3600 }, /* output next 3 messages in hour intervals */
{ 0, 12*3600 } /* repeat messages only every 12 hours */
};
static struct errorregression
err_badstatus[] = /* unsynchronized state messages */
{
{ 1, 0 }, /* output first message immediately */
{ 5, 60 }, /* output next five messages in 60 second intervals */
{ 3, 3600 }, /* output next 3 messages in hour intervals */
{ 0, 12*3600 } /* repeat messages only every 12 hours */
};
static struct errorregression
err_badio[] = /* io failures (bad reads, selects, ...) */
{
{ 1, 0 }, /* output first message immediately */
{ 5, 60 }, /* output next five messages in 60 second intervals */
{ 5, 3600 }, /* output next 3 messages in hour intervals */
{ 0, 12*3600 } /* repeat messages only every 12 hours */
};
static struct errorregression
err_badevent[] = /* non nominal events */
{
{ 20, 0 }, /* output first message immediately */
{ 6, 60 }, /* output next five messages in 60 second intervals */
{ 5, 3600 }, /* output next 3 messages in hour intervals */
{ 0, 12*3600 } /* repeat messages only every 12 hours */
};
static struct errorregression
err_internal[] = /* really bad things - basically coding/OS errors */
{
{ 0, 0 }, /* output all messages immediately */
};
static struct errorregression *
err_tbl[] =
{
err_baddata,
err_nodata,
err_badio,
err_badstatus,
err_badevent,
err_internal
};
struct errorinfo
{
u_long err_started; /* begin time (xntp) of error condition */
u_long err_last; /* last time (xntp) error occurred */
u_long err_cnt; /* number of error repititions */
u_long err_suppressed; /* number of suppressed messages */
struct errorregression *err_stage; /* current error stage */
};
/**===========================================================================
** refclock instance data
**/
struct parseunit
{
/*
* XNTP management
*/
struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
struct refclockproc *generic; /* backlink to refclockproc structure */
/*
* PARSE io
*/
bind_t *binding; /* io handling binding */
/*
* parse state
*/
parse_t parseio; /* io handling structure (user level parsing) */
/*
* type specific parameters
*/
struct parse_clockinfo *parse_type; /* link to clock description */
/*
* clock state handling/reporting
*/
u_char flags; /* flags (leap_control) */
u_long lastchange; /* time (xntp) when last state change accured */
u_long statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
struct event stattimer; /* statistics timer */
u_char pollonly; /* 1 for polling only (no PPS mode) */
u_char pollneeddata; /* 1 for receive sample expected in PPS mode */
u_long laststatus; /* last packet status (error indication) */
u_short lastformat; /* last format used */
u_long lastsync; /* time (xntp) when clock was last seen fully synchronized */
u_long lastmissed; /* time (xntp) when poll didn't get data (powerup heuristic) */
u_long ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
parsetime_t time; /* last (parse module) data */
void *localdata; /* optional local data */
struct errorinfo errors[ERR_CNT]; /* error state table for suppressing excessive error messages */
};
/**===========================================================================
** Clockinfo section all parameter for specific clock types
** includes NTP parameters, TTY parameters and IO handling parameters
**/
static void poll_dpoll P((struct parseunit *));
static void poll_poll P((struct parseunit *));
static int poll_init P((struct parseunit *));
static void poll_end P((struct parseunit *));
typedef struct poll_info
{
u_long rate; /* poll rate - once every "rate" seconds - 0 off */
char * string; /* string to send for polling */
u_long count; /* number of charcters in string */
} poll_info_t;
#define NO_CL_FLAGS 0
#define NO_POLL (void (*) P((struct parseunit *)))0
#define NO_INIT (int (*) P((struct parseunit *)))0
#define NO_END (void (*) P((struct parseunit *)))0
#define NO_EVENT (void (*) P((struct parseunit *, int)))0
#define NO_DATA (void *)0
#define NO_FORMAT ""
#define NO_PPSDELAY 0
#define DCF_ID "DCF" /* generic DCF */
#define DCF_A_ID "DCFa" /* AM demodulation */
#define DCF_P_ID "DCFp" /* psuedo random phase shift */
#define GPS_ID "GPS" /* GPS receiver */
#define NOCLOCK_ROOTDELAY 0x00000000
#define NOCLOCK_BASEDELAY 0x00000000
#define NOCLOCK_DESCRIPTION ((char *)0)
#define NOCLOCK_MAXUNSYNC 0
#define NOCLOCK_CFLAG 0
#define NOCLOCK_IFLAG 0
#define NOCLOCK_OFLAG 0
#define NOCLOCK_LFLAG 0
#define NOCLOCK_ID "TILT"
#define NOCLOCK_POLL NO_POLL
#define NOCLOCK_INIT NO_INIT
#define NOCLOCK_END NO_END
#define NOCLOCK_DATA NO_DATA
#define NOCLOCK_FORMAT NO_FORMAT
#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
#define NOCLOCK_SAMPLES 0
#define NOCLOCK_KEEP 0
#define DCF_TYPE CTL_SST_TS_LF
#define GPS_TYPE CTL_SST_TS_UHF
/*
* receiver specific constants
*/
#define MBG_SPEED (B9600)
#define MBG_CFLAG (CS7|PARENB|CREAD|CLOCAL|HUPCL)
#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
#define MBG_OFLAG 0
#define MBG_LFLAG 0
#define MBG_FLAGS PARSE_F_NOPOLLONLY
/*
* Meinberg DCF77 receivers
*/
#define DCFUA31_ROOTDELAY 0x00000000 /* 0 */
#define DCFUA31_BASEDELAY 0x02C00000 /* 10.7421875ms: 10 ms (+/- 3 ms) */
#define DCFUA31_DESCRIPTION "Meinberg DCF77 UA31/C51 or compatible"
#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
#define DCFUA31_SPEED MBG_SPEED
#define DCFUA31_CFLAG MBG_CFLAG
#define DCFUA31_IFLAG MBG_IFLAG
#define DCFUA31_OFLAG MBG_OFLAG
#define DCFUA31_LFLAG MBG_LFLAG
#define DCFUA31_SAMPLES 5
#define DCFUA31_KEEP 3
/*
* Meinberg DCF PZF535/TCXO (FM/PZF) receiver
*/
#define DCFPZF535_ROOTDELAY 0x00000000
#define DCFPZF535_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/TCXO"
#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
* @ 5e-8df/f we have accumulated
* at most 2.16 ms (thus we move to
* NTP synchronisation */
#define DCFPZF535_SPEED MBG_SPEED
#define DCFPZF535_CFLAG MBG_CFLAG
#define DCFPZF535_IFLAG MBG_IFLAG
#define DCFPZF535_OFLAG MBG_OFLAG
#define DCFPZF535_LFLAG MBG_LFLAG
#define DCFPZF535_SAMPLES 5
#define DCFPZF535_KEEP 3
/*
* Meinberg DCF PZF535/OCXO receiver
*/
#define DCFPZF535OCXO_ROOTDELAY 0x00000000
#define DCFPZF535OCXO_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/OCXO"
#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
* @ 5e-9df/f we have accumulated
* at most an error of 1.73 ms
* (thus we move to NTP synchronisation) */
#define DCFPZF535OCXO_SPEED MBG_SPEED
#define DCFPZF535OCXO_CFLAG MBG_CFLAG
#define DCFPZF535OCXO_IFLAG MBG_IFLAG
#define DCFPZF535OCXO_OFLAG MBG_OFLAG
#define DCFPZF535OCXO_LFLAG MBG_LFLAG
#define DCFPZF535OCXO_SAMPLES 32
#define DCFPZF535OCXO_KEEP 20
/*
* Meinberg GPS166 receiver
*/
#define GPS166_ROOTDELAY 0x00000000 /* nothing here */
#define GPS166_BASEDELAY 0x00800000 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
#define GPS166_DESCRIPTION "Meinberg GPS166 receiver"
#define GPS166_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
* @ 5e-9df/f we have accumulated
* at most an error of 1.73 ms
* (thus we move to NTP synchronisation) */
#define GPS166_SPEED MBG_SPEED
#define GPS166_CFLAG MBG_CFLAG
#define GPS166_IFLAG MBG_IFLAG
#define GPS166_OFLAG MBG_OFLAG
#define GPS166_LFLAG MBG_LFLAG
#define GPS166_POLL NO_POLL
#define GPS166_INIT NO_INIT
#define GPS166_END NO_END
#define GPS166_DATA NO_DATA
#define GPS166_ID GPS_ID
#define GPS166_FORMAT NO_FORMAT
#define GPS166_SAMPLES 32
#define GPS166_KEEP 20
/*
* ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
*
* This is really not the hottest clock - but before you have nothing ...
*/
#define DCF7000_ROOTDELAY 0x00000000 /* 0 */
#define DCF7000_BASEDELAY 0x67AE0000 /* 405 ms - slow blow */
#define DCF7000_DESCRIPTION "ELV DCF7000"
#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
#define DCF7000_SPEED (B9600)
#define DCF7000_CFLAG (CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
#define DCF7000_IFLAG (IGNBRK)
#define DCF7000_OFLAG 0
#define DCF7000_LFLAG 0
#define DCF7000_SAMPLES 6
#define DCF7000_KEEP 4
/*
* Schmid DCF Receiver Kit
*
* When the WSDCF clock is operating optimally we want the primary clock
* distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
* structure is set to 290 ms and we compute delays which are at least
* 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
*/
#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
#define WS_POLLCMD "\163"
#define WS_CMDSIZE 1
static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
#define WSDCF_INIT poll_init
#define WSDCF_POLL poll_dpoll
#define WSDCF_END poll_end
#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
#define WSDCF_ROOTDELAY 0X00000000 /* 0 */
#define WSDCF_BASEDELAY 0x028F5C29 /* ~ 10ms */
#define WSDCF_DESCRIPTION "WS/DCF Receiver"
#define WSDCF_FORMAT "Schmid"
#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
#define WSDCF_SPEED (B1200)
#define WSDCF_CFLAG (CS8|CREAD|CLOCAL)
#define WSDCF_IFLAG 0
#define WSDCF_OFLAG 0
#define WSDCF_LFLAG 0
#define WSDCF_SAMPLES 6
#define WSDCF_KEEP 4
/*
* RAW DCF77 - input of DCF marks via RS232 - many variants
*/
#define RAWDCF_FLAGS PARSE_F_NOPOLLONLY
#define RAWDCF_ROOTDELAY 0x00000000 /* 0 */
#define RAWDCF_FORMAT "RAW DCF77 Timecode"
#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
#define RAWDCF_SPEED (B50)
#ifdef NO_PARENB_IGNPAR /* Was: defined(SYS_IRIX4) || defined(SYS_IRIX5) */
/* somehow doesn't grok PARENB & IGNPAR (mj) */
# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
#else
# define RAWDCF_CFLAG (CS8|CREAD|CLOCAL|PARENB)
#endif
#ifdef RAWDCF_NO_IGNPAR /* Was: defined(SYS_LINUX) && defined(CLOCK_RAWDCF) */
# define RAWDCF_IFLAG 0
#else
# define RAWDCF_IFLAG (IGNPAR)
#endif
#define RAWDCF_OFLAG 0
#define RAWDCF_LFLAG 0
#define RAWDCF_SAMPLES 6
#define RAWDCF_KEEP 4
#if defined(RAWDCF_SETDTR)
static int rawdcf_init P((struct parseunit *));
#define RAWDCF_INIT rawdcf_init
#else
#define RAWDCF_INIT NO_INIT
#endif
/*
* RAW DCF variants
*/
/*
* Conrad receiver
*
* simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
* (~40DM - roughly $30 ) followed by a level converter for RS232
*/
#define CONRAD_BASEDELAY 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud on a Sun */
#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
/*
* TimeBrick receiver
*/
#define TIMEBRICK_BASEDELAY 0x35C29000 /* ~210 ms - TimeBrick @ 50 Baud on a Sun */
#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
/*
* IGEL:clock receiver
*/
#define IGELCLOCK_BASEDELAY 0x420C49B0 /* ~258 ms - IGEL:clock receiver */
#define IGELCLOCK_DESCRIPTION "RAW DCF77 CODE (IGEL:clock)"
#define IGELCLOCK_SPEED (B1200)
#define IGELCLOCK_CFLAG (CS8|CREAD|HUPCL|CLOCAL)
/*
* Trimble SV6 GPS receivers (TAIP and TSIP protocols)
*/
#define ETX 0x03
#define DLE 0x10
#ifndef TRIM_POLLRATE
#define TRIM_POLLRATE 0 /* only true direct polling */
#endif
#define TRIM_TAIPPOLLCMD ">SRM;FR_FLAG=F;EC_FLAG=F<>QTM<"
#define TRIM_TAIPCMDSIZE (sizeof(TRIM_TAIPPOLLCMD)-1)
static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
static int trimbletaip_init P((struct parseunit *));
static void trimbletaip_event P((struct parseunit *, int));
/* query time & UTC correction data */
static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
static int trimbletsip_init P((struct parseunit *));
#define TRIMBLETAIP_SPEED (B4800)
#define TRIMBLETAIP_CFLAG (CS8|CREAD|CLOCAL)
#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
#define TRIMBLETAIP_LFLAG (0)
#define TRIMBLETSIP_SPEED (B9600)
#define TRIMBLETSIP_CFLAG (CS8|CLOCAL|CREAD|PARENB|PARODD)
#define TRIMBLETSIP_IFLAG (IGNBRK)
#define TRIMBLETSIP_OFLAG (0)
#define TRIMBLETSIP_LFLAG (0)
#define TRIMBLETSIP_SAMPLES 5
#define TRIMBLETSIP_KEEP 3
#define TRIMBLETAIP_SAMPLES 5
#define TRIMBLETAIP_KEEP 3
#define TRIMBLETAIP_FLAGS (PARSE_F_PPSONSECOND)
#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS|PARSE_F_NOPOLLONLY)
#define TRIMBLETAIP_POLL poll_dpoll
#define TRIMBLETSIP_POLL poll_dpoll
#define TRIMBLETAIP_INIT trimbletaip_init
#define TRIMBLETSIP_INIT trimbletsip_init
#define TRIMBLETAIP_EVENT trimbletaip_event
#define TRIMBLETAIP_END poll_end
#define TRIMBLETSIP_END poll_end
#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
#define TRIMBLETAIP_ID GPS_ID
#define TRIMBLETSIP_ID GPS_ID
#define TRIMBLETAIP_FORMAT NO_FORMAT
#define TRIMBLETSIP_FORMAT "Trimble SV6/TSIP"
#define TRIMBLETAIP_ROOTDELAY 0x0
#define TRIMBLETSIP_ROOTDELAY 0x0
#define TRIMBLETAIP_BASEDELAY 0x0
#define TRIMBLETSIP_BASEDELAY 0x51EB852 /* 20 ms as a l_uf - avg GPS time message latency */
#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
#define TRIMBLETAIP_MAXUNSYNC 0
#define TRIMBLETSIP_MAXUNSYNC 0
#define TRIMBLETAIP_EOL '<'
/*
* RadioCode Clocks RCC 800 receiver
*/
#define RCC_POLLRATE 0 /* only true direct polling */
#define RCC_POLLCMD "\r"
#define RCC_CMDSIZE 1
static poll_info_t rcc8000_pollinfo = { RCC_POLLRATE, RCC_POLLCMD, RCC_CMDSIZE };
#define RCC8000_FLAGS
#define RCC8000_POLL poll_dpoll
#define RCC8000_INIT poll_init
#define RCC8000_END poll_end
#define RCC8000_DATA ((void *)(&rcc8000_pollinfo))
#define RCC8000_ROOTDELAY 0x0
#define RCC8000_BASEDELAY 0x0
#define RCC8000_ID "MSF"
#define RCC8000_DESCRIPTION "RCC 8000 MSF Receiver"
#define RCC8000_FORMAT NO_FORMAT
#define RCC8000_MAXUNSYNC (60*60) /* should be ok for an hour */
#define RCC8000_SPEED (B2400)
#define RCC8000_CFLAG (CS8|CREAD|CLOCAL)
#define RCC8000_IFLAG (IGNBRK|IGNPAR)
#define RCC8000_OFLAG 0
#define RCC8000_LFLAG 0
#define RCC8000_SAMPLES 5
#define RCC8000_KEEP 3
/*
* Hopf Radio clock 6021 Format
*
*/
#define HOPF6021_ROOTDELAY 0x00000000 /* 0 */
#define HOPF6021_BASEDELAY 0x00000000 /* 0 */
#define HOPF6021_DESCRIPTION "HOPF 6021"
#define HOPF6021_FORMAT "hopf Funkuhr 6021"
#define HOPF6021_MAXUNSYNC (60*60) /* should be ok for an hour */
#define HOPF6021_SPEED (B9600)
#define HOPF6021_CFLAG (CS8|CREAD|CLOCAL)
#define HOPF6021_IFLAG (IGNBRK|ISTRIP)
#define HOPF6021_OFLAG 0
#define HOPF6021_LFLAG 0
#define HOPF6021_FLAGS PARSE_F_NOPOLLONLY
#define HOPF6021_SAMPLES 5
#define HOPF6021_KEEP 3
/*
* Diem's Computime Radio Clock Receiver
*/
#define COMPUTIME_FLAGS PARSE_F_NOPOLLONLY
#define COMPUTIME_ROOTDELAY 0x00000000 /* 0 */
#define COMPUTIME_BASEDELAY 0x00000000 /* 0 */
#define COMPUTIME_ID DCF_ID
#define COMPUTIME_DESCRIPTION "Diem's Computime receiver"
#define COMPUTIME_FORMAT "Diem's Computime Radio Clock"
#define COMPUTIME_TYPE DCF_TYPE
#define COMPUTIME_MAXUNSYNC (60*60) /* only trust clock for 1 hour */
#define COMPUTIME_SPEED (B9600)
#define COMPUTIME_CFLAG (CSTOPB|CS7|CREAD|CLOCAL)
#define COMPUTIME_IFLAG (IGNBRK|IGNPAR|ISTRIP)
#define COMPUTIME_OFLAG 0
#define COMPUTIME_LFLAG 0
#define COMPUTIME_SAMPLES 5
#define COMPUTIME_KEEP 3
static struct parse_clockinfo
{
u_long cl_flags; /* operation flags (io modes) */
void (*cl_poll) /* active poll routine */
P((struct parseunit *));
int (*cl_init) /* active poll init routine */
P((struct parseunit *));
void (*cl_event) /* special event handling (e.g. reset clock) */
P((struct parseunit *, int));
void (*cl_end) /* active poll end routine */
P((struct parseunit *));
void *cl_data; /* local data area for "poll" mechanism */
u_fp cl_rootdelay; /* rootdelay */
u_long cl_basedelay; /* current offset - unsigned l_fp fractional part */
u_long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional part */
char *cl_id; /* ID code */
char *cl_description; /* device name */
char *cl_format; /* fixed format */
u_char cl_type; /* clock type (ntp control) */
u_long cl_maxunsync; /* time to trust oscillator after loosing synch */
u_long cl_speed; /* terminal input & output baudrate */
u_long cl_cflag; /* terminal control flags */
u_long cl_iflag; /* terminal input flags */
u_long cl_oflag; /* terminal output flags */
u_long cl_lflag; /* terminal local flags */
u_long cl_samples; /* samples for median filter */
u_long cl_keep; /* samples for median filter to keep */
} parse_clockinfo[] =
{
{ /* mode 0 */
MBG_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
DCFPZF535_ROOTDELAY,
DCFPZF535_BASEDELAY,
NO_PPSDELAY,
DCF_P_ID,
DCFPZF535_DESCRIPTION,
NO_FORMAT,
DCF_TYPE,
DCFPZF535_MAXUNSYNC,
DCFPZF535_SPEED,
DCFPZF535_CFLAG,
DCFPZF535_IFLAG,
DCFPZF535_OFLAG,
DCFPZF535_LFLAG,
DCFPZF535_SAMPLES,
DCFPZF535_KEEP
},
{ /* mode 1 */
MBG_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
DCFPZF535OCXO_ROOTDELAY,
DCFPZF535OCXO_BASEDELAY,
NO_PPSDELAY,
DCF_P_ID,
DCFPZF535OCXO_DESCRIPTION,
NO_FORMAT,
DCF_TYPE,
DCFPZF535OCXO_MAXUNSYNC,
DCFPZF535OCXO_SPEED,
DCFPZF535OCXO_CFLAG,
DCFPZF535OCXO_IFLAG,
DCFPZF535OCXO_OFLAG,
DCFPZF535OCXO_LFLAG,
DCFPZF535OCXO_SAMPLES,
DCFPZF535OCXO_KEEP
},
{ /* mode 2 */
MBG_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
DCFUA31_ROOTDELAY,
DCFUA31_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
DCFUA31_DESCRIPTION,
NO_FORMAT,
DCF_TYPE,
DCFUA31_MAXUNSYNC,
DCFUA31_SPEED,
DCFUA31_CFLAG,
DCFUA31_IFLAG,
DCFUA31_OFLAG,
DCFUA31_LFLAG,
DCFUA31_SAMPLES,
DCFUA31_KEEP
},
{ /* mode 3 */
MBG_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
DCF7000_ROOTDELAY,
DCF7000_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
DCF7000_DESCRIPTION,
NO_FORMAT,
DCF_TYPE,
DCF7000_MAXUNSYNC,
DCF7000_SPEED,
DCF7000_CFLAG,
DCF7000_IFLAG,
DCF7000_OFLAG,
DCF7000_LFLAG,
DCF7000_SAMPLES,
DCF7000_KEEP
},
{ /* mode 4 */
NO_CL_FLAGS,
WSDCF_POLL,
WSDCF_INIT,
NO_EVENT,
WSDCF_END,
WSDCF_DATA,
WSDCF_ROOTDELAY,
WSDCF_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
WSDCF_DESCRIPTION,
WSDCF_FORMAT,
DCF_TYPE,
WSDCF_MAXUNSYNC,
WSDCF_SPEED,
WSDCF_CFLAG,
WSDCF_IFLAG,
WSDCF_OFLAG,
WSDCF_LFLAG,
WSDCF_SAMPLES,
WSDCF_KEEP
},
{ /* mode 5 */
RAWDCF_FLAGS,
NO_POLL,
RAWDCF_INIT,
NO_EVENT,
NO_END,
NO_DATA,
RAWDCF_ROOTDELAY,
CONRAD_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
CONRAD_DESCRIPTION,
RAWDCF_FORMAT,
DCF_TYPE,
RAWDCF_MAXUNSYNC,
RAWDCF_SPEED,
RAWDCF_CFLAG,
RAWDCF_IFLAG,
RAWDCF_OFLAG,
RAWDCF_LFLAG,
RAWDCF_SAMPLES,
RAWDCF_KEEP
},
{ /* mode 6 */
RAWDCF_FLAGS,
NO_POLL,
RAWDCF_INIT,
NO_EVENT,
NO_END,
NO_DATA,
RAWDCF_ROOTDELAY,
TIMEBRICK_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
TIMEBRICK_DESCRIPTION,
RAWDCF_FORMAT,
DCF_TYPE,
RAWDCF_MAXUNSYNC,
RAWDCF_SPEED,
RAWDCF_CFLAG,
RAWDCF_IFLAG,
RAWDCF_OFLAG,
RAWDCF_LFLAG,
RAWDCF_SAMPLES,
RAWDCF_KEEP
},
{ /* mode 7 */
MBG_FLAGS,
GPS166_POLL,
GPS166_INIT,
NO_EVENT,
GPS166_END,
GPS166_DATA,
GPS166_ROOTDELAY,
GPS166_BASEDELAY,
NO_PPSDELAY,
GPS166_ID,
GPS166_DESCRIPTION,
GPS166_FORMAT,
GPS_TYPE,
GPS166_MAXUNSYNC,
GPS166_SPEED,
GPS166_CFLAG,
GPS166_IFLAG,
GPS166_OFLAG,
GPS166_LFLAG,
GPS166_SAMPLES,
GPS166_KEEP
},
{ /* mode 8 */
RAWDCF_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
RAWDCF_ROOTDELAY,
IGELCLOCK_BASEDELAY,
NO_PPSDELAY,
DCF_A_ID,
IGELCLOCK_DESCRIPTION,
RAWDCF_FORMAT,
DCF_TYPE,
RAWDCF_MAXUNSYNC,
IGELCLOCK_SPEED,
IGELCLOCK_CFLAG,
RAWDCF_IFLAG,
RAWDCF_OFLAG,
RAWDCF_LFLAG,
RAWDCF_SAMPLES,
RAWDCF_KEEP
},
{ /* mode 9 */
TRIMBLETAIP_FLAGS,
#if TRIM_POLLRATE /* DHD940515: Allow user config */
NO_POLL,
#else
TRIMBLETAIP_POLL,
#endif
TRIMBLETAIP_INIT,
TRIMBLETAIP_EVENT,
TRIMBLETAIP_END,
TRIMBLETAIP_DATA,
TRIMBLETAIP_ROOTDELAY,
TRIMBLETAIP_BASEDELAY,
NO_PPSDELAY,
TRIMBLETAIP_ID,
TRIMBLETAIP_DESCRIPTION,
TRIMBLETAIP_FORMAT,
GPS_TYPE,
TRIMBLETAIP_MAXUNSYNC,
TRIMBLETAIP_SPEED,
TRIMBLETAIP_CFLAG,
TRIMBLETAIP_IFLAG,
TRIMBLETAIP_OFLAG,
TRIMBLETAIP_LFLAG,
TRIMBLETAIP_SAMPLES,
TRIMBLETAIP_KEEP
},
{ /* mode 10 */
TRIMBLETSIP_FLAGS,
#if TRIM_POLLRATE /* DHD940515: Allow user config */
NO_POLL,
#else
TRIMBLETSIP_POLL,
#endif
TRIMBLETSIP_INIT,
NO_EVENT,
TRIMBLETSIP_END,
TRIMBLETSIP_DATA,
TRIMBLETSIP_ROOTDELAY,
TRIMBLETSIP_BASEDELAY,
NO_PPSDELAY,
TRIMBLETSIP_ID,
TRIMBLETSIP_DESCRIPTION,
TRIMBLETSIP_FORMAT,
GPS_TYPE,
TRIMBLETSIP_MAXUNSYNC,
TRIMBLETSIP_SPEED,
TRIMBLETSIP_CFLAG,
TRIMBLETSIP_IFLAG,
TRIMBLETSIP_OFLAG,
TRIMBLETSIP_LFLAG,
TRIMBLETSIP_SAMPLES,
TRIMBLETSIP_KEEP
},
{ /* mode 11 */
NO_CL_FLAGS,
RCC8000_POLL,
RCC8000_INIT,
NO_EVENT,
RCC8000_END,
RCC8000_DATA,
RCC8000_ROOTDELAY,
RCC8000_BASEDELAY,
NO_PPSDELAY,
RCC8000_ID,
RCC8000_DESCRIPTION,
RCC8000_FORMAT,
DCF_TYPE,
RCC8000_MAXUNSYNC,
RCC8000_SPEED,
RCC8000_CFLAG,
RCC8000_IFLAG,
RCC8000_OFLAG,
RCC8000_LFLAG,
RCC8000_SAMPLES,
RCC8000_KEEP
},
{ /* mode 12 */
HOPF6021_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
HOPF6021_ROOTDELAY,
HOPF6021_BASEDELAY,
NO_PPSDELAY,
DCF_ID,
HOPF6021_DESCRIPTION,
HOPF6021_FORMAT,
DCF_TYPE,
HOPF6021_MAXUNSYNC,
HOPF6021_SPEED,
HOPF6021_CFLAG,
HOPF6021_IFLAG,
HOPF6021_OFLAG,
HOPF6021_LFLAG,
HOPF6021_SAMPLES,
HOPF6021_KEEP
},
{ /* mode 13 */
COMPUTIME_FLAGS,
NO_POLL,
NO_INIT,
NO_EVENT,
NO_END,
NO_DATA,
COMPUTIME_ROOTDELAY,
COMPUTIME_BASEDELAY,
NO_PPSDELAY,
COMPUTIME_ID,
COMPUTIME_DESCRIPTION,
COMPUTIME_FORMAT,
COMPUTIME_TYPE,
COMPUTIME_MAXUNSYNC,
COMPUTIME_SPEED,
COMPUTIME_CFLAG,
COMPUTIME_IFLAG,
COMPUTIME_OFLAG,
COMPUTIME_LFLAG,
COMPUTIME_SAMPLES,
COMPUTIME_KEEP
}
};
static int ncltypes = sizeof(parse_clockinfo) / sizeof(struct parse_clockinfo);
#define CLK_REALTYPE(x) ((int)(((x)->ttl) & 0x7F))
#define CLK_TYPE(x) ((CLK_REALTYPE(x) >= ncltypes) ? ~0 : CLK_REALTYPE(x))
#define CLK_UNIT(x) (REFCLOCKUNIT(&(x)->srcadr))
#define CLK_PPS(x) (((x)->ttl) & 0x80)
/*
* Other constant stuff
*/
#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
#define PARSESTATISTICS (60*60) /* output state statistics every hour */
static struct parseunit *parseunits[MAXUNITS];
extern u_long current_time;
extern s_char sys_precision;
extern struct event timerqueue[];
#ifdef ATOM
extern int fdpps;
extern int pps_sample P((l_fp *));
#endif
static int notice = 0;
#define PARSE_STATETIME(parse, i) ((parse->generic->currentstatus == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
static void parse_event P((struct parseunit *, int));
static void parse_process P((struct parseunit *, parsetime_t *));
static void clear_err P((struct parseunit *, u_long));
static int list_err P((struct parseunit *, u_long));
static char * l_mktime P((u_long));
/**===========================================================================
** implementation error message regression module
**/
static void
clear_err(parse, state)
struct parseunit *parse;
u_long state;
{
if (state == ERR_ALL)
{
int i;
for (i = 0; i < ERR_CNT; i++)
{
parse->errors[i].err_stage = err_tbl[i];
parse->errors[i].err_cnt = 0;
parse->errors[i].err_last = 0;
parse->errors[i].err_started = 0;
parse->errors[i].err_suppressed = 0;
}
}
else
{
parse->errors[state].err_stage = err_tbl[state];
parse->errors[state].err_cnt = 0;
parse->errors[state].err_last = 0;
parse->errors[state].err_started = 0;
parse->errors[state].err_suppressed = 0;
}
}
static int
list_err(parse, state)
struct parseunit *parse;
u_long state;
{
int do_it;
struct errorinfo *err = &parse->errors[state];
if (err->err_started == 0)
{
err->err_started = current_time;
}
do_it = (current_time - err->err_last) >= err->err_stage->err_delay;
if (do_it)
err->err_cnt++;
if (err->err_stage->err_count &&
(err->err_cnt >= err->err_stage->err_count))
{
err->err_stage++;
err->err_cnt = 0;
}
if (!err->err_cnt && do_it)
msyslog(LOG_INFO, "PARSE receiver #%d: interval for following error message class is at least %s",
CLK_UNIT(parse->peer), l_mktime(err->err_stage->err_delay));
if (!do_it)
err->err_suppressed++;
else
err->err_last = current_time;
if (do_it && err->err_suppressed)
{
msyslog(LOG_INFO, "PARSE receiver #%d: %d message%s suppressed, error condition class persists for %s",
CLK_UNIT(parse->peer), err->err_suppressed, (err->err_suppressed == 1) ? " was" : "s where",
l_mktime(current_time - err->err_started));
err->err_suppressed = 0;
}
return do_it;
}
/**===========================================================================
** implementation of i/o handling methods
** (all STREAM, partial STREAM, user level)
**/
/*
* define possible io handling methods
*/
#ifdef STREAM
static int ppsclock_init P((struct parseunit *));
static int stream_init P((struct parseunit *));
static void stream_end P((struct parseunit *));
static int stream_enable P((struct parseunit *));
static int stream_disable P((struct parseunit *));
static int stream_setcs P((struct parseunit *, parsectl_t *));
static int stream_getfmt P((struct parseunit *, parsectl_t *));
static int stream_setfmt P((struct parseunit *, parsectl_t *));
static int stream_timecode P((struct parseunit *, parsectl_t *));
static void stream_receive P((struct recvbuf *));
static void stream_poll P((struct parseunit *));
#endif
static int local_init P((struct parseunit *));
static void local_end P((struct parseunit *));
static int local_nop P((struct parseunit *));
static int local_setcs P((struct parseunit *, parsectl_t *));
static int local_getfmt P((struct parseunit *, parsectl_t *));
static int local_setfmt P((struct parseunit *, parsectl_t *));
static int local_timecode P((struct parseunit *, parsectl_t *));
static void local_receive P((struct recvbuf *));
static void local_poll P((struct parseunit *));
static bind_t *init_iobinding P((struct parseunit *));
static char *parsestate P((u_long, char *));
static char *parsestatus P((u_long, char *));
static char *clockstatus P((u_long));
static char *mkascii P((char *, long, char *, long));
static void parse_statistics P((struct parseunit *));
static void cparse_statistics P((struct peer *));
static bind_t io_bindings[] =
{
#ifdef STREAM
{
"parse STREAM",
stream_init,
stream_end,
stream_setcs,
stream_disable,
stream_enable,
stream_getfmt,
stream_setfmt,
stream_timecode,
stream_receive,
stream_poll
},
{
"ppsclock STREAM",
ppsclock_init,
local_end,
local_setcs,
local_nop,
local_nop,
local_getfmt,
local_setfmt,
local_timecode,
local_receive,
local_poll
},
#endif
{
"normal",
local_init,
local_end,
local_setcs,
local_nop,
local_nop,
local_getfmt,
local_setfmt,
local_timecode,
local_receive,
local_poll
},
{
(char *)0,
}
};
#ifdef STREAM
#define fix_ts(_X_) \
if ((&(_X_))->tv.tv_usec >= 1000000) \
{ \
(&(_X_))->tv.tv_usec -= 1000000; \
(&(_X_))->tv.tv_sec += 1; \
}
#define cvt_ts(_X_, _Y_) \
{ \
l_fp ts; \
\
fix_ts((_X_)); \
if (!buftvtots((const char *)&(&(_X_))->tv, &ts)) \
{ \
ERR(ERR_BADDATA) \
msyslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%d.%06d) ", (_Y_), (&(_X_))->tv.tv_sec, (&(_X_))->tv.tv_usec);\
return; \
} \
else \
{ \
(&(_X_))->fp = ts; \
} \
}
/*--------------------------------------------------
* ppsclock STREAM init
*/
static int
ppsclock_init(parse)
struct parseunit *parse;
{
/*
* now push the parse streams module
* it will ensure exclusive access to the device
*/
if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)"ppsclocd") == -1 &&
ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)"ppsclock") == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
CLK_UNIT(parse->peer));
return 0;
}
if (!local_init(parse))
{
(void)ioctl(parse->generic->io.fd, I_POP, (caddr_t)0);
return 0;
}
parse->flags |= PARSE_PPSCLOCK;
return 1;
}
/*--------------------------------------------------
* parse STREAM init
*/
static int
stream_init(parse)
struct parseunit *parse;
{
/*
* now push the parse streams module
* to test whether it is there (Oh boy - neat kernel interface)
*/
if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)"parse") == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
return 0;
}
else
{
while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
/* empty loop */;
/*
* now push it a second time after we have removed all
* module garbage
*/
if (ioctl(parse->generic->io.fd, I_PUSH, (caddr_t)"parse") == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CLK_UNIT(parse->peer));
return 0;
}
else
{
return 1;
}
}
}
/*--------------------------------------------------
* parse STREAM end
*/
static void
stream_end(parse)
struct parseunit *parse;
{
while(ioctl(parse->generic->io.fd, I_POP, (caddr_t)0) == 0)
/* empty loop */;
}
/*--------------------------------------------------
* STREAM setcs
*/
static int
stream_setcs(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_SETCS;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)tcl;
strioc.ic_len = sizeof (*tcl);
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CLK_UNIT(parse->peer));
return 0;
}
return 1;
}
/*--------------------------------------------------
* STREAM enable
*/
static int
stream_enable(parse)
struct parseunit *parse;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_ENABLE;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)0;
strioc.ic_len = 0;
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CLK_UNIT(parse->peer));
return 0;
}
parse->generic->io.clock_recv = stream_receive; /* ok - parse input in kernel */
return 1;
}
/*--------------------------------------------------
* STREAM disable
*/
static int
stream_disable(parse)
struct parseunit *parse;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_DISABLE;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)0;
strioc.ic_len = 0;
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CLK_UNIT(parse->peer));
return 0;
}
parse->generic->io.clock_recv = local_receive; /* ok - parse input in daemon */
return 1;
}
/*--------------------------------------------------
* STREAM getfmt
*/
static int
stream_getfmt(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_GETFMT;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)tcl;
strioc.ic_len = sizeof (*tcl);
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CLK_UNIT(parse->peer));
return 0;
}
return 1;
}
/*--------------------------------------------------
* STREAM setfmt
*/
static int
stream_setfmt(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_SETFMT;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)tcl;
strioc.ic_len = sizeof (*tcl);
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CLK_UNIT(parse->peer));
return 0;
}
return 1;
}
/*--------------------------------------------------
* STREAM timecode
*/
static int
stream_timecode(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
struct strioctl strioc;
strioc.ic_cmd = PARSEIOC_TIMECODE;
strioc.ic_timout = 0;
strioc.ic_dp = (char *)tcl;
strioc.ic_len = sizeof (*tcl);
if (ioctl(parse->generic->io.fd, I_STR, (caddr_t)&strioc) == -1)
{
ERR(ERR_INTERNAL)
msyslog(LOG_ERR, "PARSE receiver #%d: stream_timecode: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CLK_UNIT(parse->peer));
return 0;
}
clear_err(parse, ERR_INTERNAL);
return 1;
}
/*--------------------------------------------------
* STREAM receive
*/
static void
stream_receive(rbufp)
struct recvbuf *rbufp;
{
struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
parsetime_t parsetime;
if (!parse->peer)
return;
if (rbufp->recv_length != sizeof(parsetime_t))
{
ERR(ERR_BADIO)
msyslog(LOG_ERR,"PARSE receiver #%d: parse_receive: bad size (got %d expected %d)",
CLK_UNIT(parse->peer), rbufp->recv_length, sizeof(parsetime_t));
parse->generic->baddata++;
parse_event(parse, CEVNT_BADREPLY);
return;
}
clear_err(parse, ERR_BADIO);
memmove((caddr_t)&parsetime,
(caddr_t)rbufp->recv_buffer,
sizeof(parsetime_t));
/*
* switch time stamp world - be sure to normalize small usec field
* errors.
*/
cvt_ts(parsetime.parse_stime, "parse_stime");
if (PARSE_TIMECODE(parsetime.parse_state))
{
cvt_ts(parsetime.parse_time, "parse_time");
}
if (PARSE_PPS(parsetime.parse_state))
cvt_ts(parsetime.parse_ptime, "parse_ptime");
parse_process(parse, &parsetime);
}
/*--------------------------------------------------
* STREAM poll
*/
static void
stream_poll(parse)
struct parseunit *parse;
{
int fd, i, rtc;
fd_set fdmask;
struct timeval timeout, starttime, curtime, selecttime;
#ifdef HAVE_GETCLOCK
struct timespec ts;
#endif
parsetime_t parsetime;
/*
* now we do the following:
* - read the first packet from the parse module (OLD !!!)
* - read the second packet from the parse module (fresh)
* - compute values for xntp
*/
FD_ZERO(&fdmask);
fd = parse->generic->io.fd;
FD_SET(fd, &fdmask);
timeout.tv_sec = 0;
timeout.tv_usec = 500000; /* 0.5 sec */
if (parse->parse_type->cl_poll)
{
parse->parse_type->cl_poll(parse);
}
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR,"getclock failed: %m");
exit(1);
}
starttime.tv_sec = ts.tv_sec;
starttime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&starttime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime = timeout;
while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
{
/* no data from the radio clock */
if (rtc == -1)
{
if (errno == EINTR)
{
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR,"getclock failed: %m");
exit(1);
}
curtime.tv_sec = ts.tv_sec;
curtime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&curtime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
if (curtime.tv_usec < starttime.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
}
else
{
selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
}
if (timercmp(&selecttime, &timeout, >))
{
/*
* elapsed real time passed timeout value - consider it timed out
*/
break;
}
/*
* calculate residual timeout value
*/
selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
if (selecttime.tv_usec > timeout.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
}
else
{
selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
}
FD_SET(fd, &fdmask);
continue;
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (select() error: %m)", CLK_UNIT(parse->peer));
}
}
else
{
ERR(ERR_NODATA)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (check receiver / cableling)", CLK_UNIT(parse->peer));
}
parse->generic->noreply++;
parse->lastmissed = current_time;
parse_event(parse, CEVNT_TIMEOUT);
return;
}
while (((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
{
/* bad packet */
if ( i == -1)
{
if (errno == EINTR)
{
continue;
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (read() error: %m)", CLK_UNIT(parse->peer), i, sizeof(parsetime));
}
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (got %d bytes - expected %d bytes)", CLK_UNIT(parse->peer), i, sizeof(parsetime));
}
parse->generic->baddata++;
parse_event(parse, CEVNT_BADREPLY);
return;
}
if (parse->parse_type->cl_poll)
{
parse->parse_type->cl_poll(parse);
}
timeout.tv_sec = 1;
timeout.tv_usec = 500000; /* 1.500 sec */
FD_ZERO(&fdmask);
FD_SET(fd, &fdmask);
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR,"getclock failed: %m");
exit(1);
}
starttime.tv_sec = ts.tv_sec;
starttime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&starttime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime = timeout;
while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
{
/* no data from the radio clock */
if (rtc == -1)
{
if (errno == EINTR)
{
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR, "getclock failed: %m");
exit(1);
}
curtime.tv_sec = ts.tv_sec;
curtime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&curtime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
if (curtime.tv_usec < starttime.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
}
else
{
selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
}
if (timercmp(&selecttime, &timeout, >))
{
/*
* elapsed real time passed timeout value - consider it timed out
*/
break;
}
/*
* calculate residual timeout value
*/
selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
if (selecttime.tv_usec > timeout.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
}
else
{
selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
}
FD_SET(fd, &fdmask);
continue;
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (select() error: %m)", CLK_UNIT(parse->peer));
}
}
else
{
ERR(ERR_NODATA)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (check receiver / cableling) ", CLK_UNIT(parse->peer));
}
/*
* we will return here iff we got a good old sample as this would
* be misinterpreted. bad samples are passed on to be logged into the
* state statistics
*/
if ((parsetime.parse_status & CVT_MASK) == CVT_OK)
{
parse->generic->noreply++;
parse->lastmissed = current_time;
parse_event(parse, CEVNT_TIMEOUT);
return;
}
}
/*
* we get here either by a possible read() (rtc == 1 - while assertion)
* or by a timeout or a system call error. when a read() is possible we
* get the new data, otherwise we stick with the old
*/
if ((rtc == 1) && ((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
{
/* bad packet */
if ( i== -1)
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (read() error: %m)", CLK_UNIT(parse->peer), i, sizeof(parsetime));
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (got %d bytes - expected %d bytes)", CLK_UNIT(parse->peer), i, sizeof(parsetime));
}
parse->generic->baddata++;
parse_event(parse, CEVNT_BADREPLY);
return;
}
cvt_ts(parsetime.parse_stime, "parse_stime");
if (PARSE_TIMECODE(parsetime.parse_state))
{
cvt_ts(parsetime.parse_time, "parse_time");
}
if (PARSE_PPS(parsetime.parse_state))
cvt_ts(parsetime.parse_ptime, "parse_ptime");
/*
* process what we got
*/
parse_process(parse, &parsetime);
}
#endif
/*--------------------------------------------------
* local init
*/
static int
local_init(parse)
struct parseunit *parse;
{
return parse_ioinit(&parse->parseio);
}
/*--------------------------------------------------
* local end
*/
static void
local_end(parse)
struct parseunit *parse;
{
parse_ioend(&parse->parseio);
}
/*--------------------------------------------------
* local nop
*/
static int
local_nop(parse)
struct parseunit *parse;
{
return 1;
}
/*--------------------------------------------------
* local setcs
*/
static int
local_setcs(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
return parse_setcs(tcl, &parse->parseio);
}
/*--------------------------------------------------
* local getfmt
*/
static int
local_getfmt(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
return parse_getfmt(tcl, &parse->parseio);
}
/*--------------------------------------------------
* local setfmt
*/
static int
local_setfmt(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
return parse_setfmt(tcl, &parse->parseio);
}
/*--------------------------------------------------
* local timecode
*/
static int
local_timecode(parse, tcl)
struct parseunit *parse;
parsectl_t *tcl;
{
return parse_timecode(tcl, &parse->parseio);
}
/*--------------------------------------------------
* local receive
*/
static void
local_receive(rbufp)
struct recvbuf *rbufp;
{
struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
int count;
unsigned char *s;
timestamp_t ts;
if (!parse->peer)
return;
/*
* eat all characters, parsing then and feeding complete samples
*/
count = rbufp->recv_length;
s = (unsigned char *)rbufp->recv_buffer;
ts.fp = rbufp->recv_time;
while (count--)
{
if (parse_ioread(&parse->parseio, (unsigned int)(*s++), &ts))
{
/*
* got something good to eat
*/
#ifdef PPS
if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state) &&
(parse->flags & PARSE_PPSCLOCK))
{
l_fp ts;
struct ppsclockev ev;
if (ioctl(parse->generic->io.fd, CIOGETEV, (caddr_t)&ev) == 0)
{
if (ev.serial != parse->ppsserial)
{
/*
* add PPS time stamp if available via ppsclock module
* and not supplied already.
*/
if (!buftvtots((const char *)&ev.tv, &ts))
{
ERR(ERR_BADDATA)
msyslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
}
else
{
parse->parseio.parse_dtime.parse_ptime.fp = ts;
parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
}
}
parse->ppsserial = ev.serial;
}
}
#endif
parse_process(parse, &parse->parseio.parse_dtime);
parse_iodone(&parse->parseio);
}
}
}
/*--------------------------------------------------
* local poll
*/
static void
local_poll(parse)
struct parseunit *parse;
{
int fd, i, rtc;
fd_set fdmask;
struct timeval timeout, starttime, curtime, selecttime;
#ifdef HAVE_GETCLOCK
struct timespec ts;
#endif
static struct timeval null_time = { 0, 0};
timestamp_t st;
FD_ZERO(&fdmask);
fd = parse->generic->io.fd;
FD_SET(fd, &fdmask);
timeout.tv_sec = 1;
timeout.tv_usec = 500000; /* 1.5 sec */
if (parse->parse_type->cl_poll)
{
parse->parse_type->cl_poll(parse);
}
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR, "getclock failed: %m");
exit(1);
}
starttime.tv_sec = ts.tv_sec;
starttime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&starttime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime = timeout;
do
{
while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
{
/* no data from the radio clock */
if (rtc == -1)
{
if (errno == EINTR)
{
#ifdef HAVE_GETCLOCK
if (getclock(TIMEOFDAY, &ts) == -1)
{
msyslog(LOG_ERR, "getclock failed: %m");
exit(1);
}
curtime.tv_sec = ts.tv_sec;
curtime.tv_usec = ts.tv_nsec / 1000;
#else /* not HAVE_GETCLOCK */
if (GETTIMEOFDAY(&curtime, 0L) == -1)
{
msyslog(LOG_ERR,"gettimeofday failed: %m");
exit(1);
}
#endif /* not HAVE_GETCLOCK */
selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
if (curtime.tv_usec < starttime.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
}
else
{
selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
}
if (!timercmp(&selecttime, &timeout, >))
{
/*
* calculate residual timeout value
*/
selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
if (selecttime.tv_usec > timeout.tv_usec)
{
selecttime.tv_sec -= 1;
selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
}
else
{
selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
}
FD_SET(fd, &fdmask);
continue;
}
}
else
{
ERR(ERR_BADIO)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device (select() error: %m)", CLK_UNIT(parse->peer));
}
}
else
{
ERR(ERR_NODATA)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device (check receiver / cableling) ", CLK_UNIT(parse->peer));
}
parse->generic->noreply++;
parse->lastmissed = current_time;
parse_event(parse, CEVNT_TIMEOUT);
return;
}
/*
* at least 1 character is available - gobble everthing up that is available
*/
do
{
char inbuf[256];
char *s = inbuf;
rtc = i = read(fd, inbuf, sizeof(inbuf));
get_systime(&st.fp);
while (i-- > 0)
{
if (parse_ioread(&parse->parseio, (unsigned int)(*s++), &st))
{
/*
* got something good to eat
*/
parse_process(parse, &parse->parseio.parse_dtime);
parse_iodone(&parse->parseio);
/*
* done if no more characters are available
*/
FD_SET(fd, &fdmask);
if ((i == 0) &&
(select(fd + 1, &fdmask, 0, 0, &null_time) == 0))
return;
}
}
FD_SET(fd, &fdmask);
} while ((rtc = select(fd + 1, &fdmask, 0, 0, &null_time)) == 1);
FD_SET(fd, &fdmask);
} while (1);
}
/*--------------------------------------------------
* init_iobinding - find and initialize lower layers
*/
static bind_t *
init_iobinding(parse)
struct parseunit *parse;
{
bind_t *b = io_bindings;
while (b->bd_description != (char *)0)
{
if ((*b->bd_init)(parse))
{
return b;
}
b++;
}
return (bind_t *)0;
}
/**===========================================================================
** support routines
**/
/*--------------------------------------------------
* convert a flag field to a string
*/
static char *
parsestate(state, buffer)
u_long state;
char *buffer;
{
static struct bits
{
u_long bit;
char *name;
} flagstrings[] =
{
{ PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
{ PARSEB_POWERUP, "NOT SYNCHRONIZED" },
{ PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
{ PARSEB_DST, "DST" },
{ PARSEB_UTC, "UTC DISPLAY" },
{ PARSEB_LEAPADD, "LEAP ADD WARNING" },
{ PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
{ PARSEB_LEAPSECOND, "LEAP SECOND" },
{ PARSEB_ALTERNATE,"ALTERNATE ANTENNA" },
{ PARSEB_TIMECODE, "TIME CODE" },
{ PARSEB_PPS, "PPS" },
{ PARSEB_POSITION, "POSITION" },
{ 0 }
};
static struct sbits
{
u_long bit;
char *name;
} sflagstrings[] =
{
{ PARSEB_S_LEAP, "LEAP INDICATION" },
{ PARSEB_S_PPS, "PPS SIGNAL" },
{ PARSEB_S_ANTENNA, "ANTENNA" },
{ PARSEB_S_POSITION, "POSITION" },
{ 0 }
};
int i;
*buffer = '\0';
i = 0;
while (flagstrings[i].bit)
{
if (flagstrings[i].bit & state)
{
if (buffer[0])
strcat(buffer, "; ");
strcat(buffer, flagstrings[i].name);
}
i++;
}
if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
{
char *s, *t;
if (buffer[0])
strcat(buffer, "; ");
strcat(buffer, "(");
t = s = buffer + strlen(buffer);
i = 0;
while (sflagstrings[i].bit)
{
if (sflagstrings[i].bit & state)
{
if (t != s)
{
strcpy(t, "; ");
t += 2;
}
strcpy(t, sflagstrings[i].name);
t += strlen(t);
}
i++;
}
strcpy(t, ")");
}
return buffer;
}
/*--------------------------------------------------
* convert a status flag field to a string
*/
static char *
parsestatus(state, buffer)
u_long state;
char *buffer;
{
static struct bits
{
u_long bit;
char *name;
} flagstrings[] =
{
{ CVT_OK, "CONVERSION SUCCESSFUL" },
{ CVT_NONE, "NO CONVERSION" },
{ CVT_FAIL, "CONVERSION FAILED" },
{ CVT_BADFMT, "ILLEGAL FORMAT" },
{ CVT_BADDATE, "DATE ILLEGAL" },
{ CVT_BADTIME, "TIME ILLEGAL" },
{ 0 }
};
int i;
*buffer = '\0';
i = 0;
while (flagstrings[i].bit)
{
if (flagstrings[i].bit & state)
{
if (buffer[0])
strcat(buffer, "; ");
strcat(buffer, flagstrings[i].name);
}
i++;
}
return buffer;
}
/*--------------------------------------------------
* convert a clock status flag field to a string
*/
static char *
clockstatus(state)
u_long state;
{
static char buffer[20];
static struct status
{
u_long value;
char *name;
} flagstrings[] =
{
{ CEVNT_NOMINAL, "NOMINAL" },
{ CEVNT_TIMEOUT, "NO RESPONSE" },
{ CEVNT_BADREPLY,"BAD FORMAT" },
{ CEVNT_FAULT, "FAULT" },
{ CEVNT_PROP, "PROPAGATION DELAY" },
{ CEVNT_BADDATE, "ILLEGAL DATE" },
{ CEVNT_BADTIME, "ILLEGAL TIME" },
{ ~0UL }
};
int i;
i = 0;
while (flagstrings[i].value != ~0)
{
if (flagstrings[i].value == state)
{
return flagstrings[i].name;
}
i++;
}
sprintf(buffer, "unknown #%ld", (u_long)state);
return buffer;
}
/*--------------------------------------------------
* mkascii - make a printable ascii string
* assumes (unless defined better) 7-bit ASCII
*/
#ifndef isprint
#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
#endif
static char *
mkascii(buffer, blen, src, srclen)
char *buffer;
long blen;
char *src;
long srclen;
{
char *b = buffer;
char *endb = (char *)0;
if (blen < 4)
return (char *)0; /* don't bother with mini buffers */
endb = buffer + blen - 4;
blen--; /* account for '\0' */
while (blen && srclen--)
{
if ((*src != '\\') && isprint(*src))
{ /* printables are easy... */
*buffer++ = *src++;
blen--;
}
else
{
if (blen < 4)
{
while (blen--)
{
*buffer++ = '.';
}
*buffer = '\0';
return b;
}
else
{
if (*src == '\\')
{
strcpy(buffer,"\\\\");
buffer += 2;
blen -= 2;
}
else
{
sprintf(buffer, "\\x%02x", *src++);
blen -= 4;
buffer += 4;
}
}
}
if (srclen && !blen && endb) /* overflow - set last chars to ... */
strcpy(endb, "...");
}
*buffer = '\0';
return b;
}
/*--------------------------------------------------
* l_mktime - make representation of a relative time
*/
static char *
l_mktime(delta)
u_long delta;
{
u_long tmp, m, s;
static char buffer[40];
buffer[0] = '\0';
if ((tmp = delta / (60*60*24)) != 0)
{
sprintf(buffer, "%ldd+", (u_long)tmp);
delta -= tmp * 60*60*24;
}
s = delta % 60;
delta /= 60;
m = delta % 60;
delta /= 60;
sprintf(buffer+strlen(buffer), "%02d:%02d:%02d",
(int)delta, (int)m, (int)s);
return buffer;
}
/*--------------------------------------------------
* parse_statistics - list summary of clock states
*/
static void
parse_statistics(parse)
struct parseunit *parse;
{
int i;
NLOG(NLOG_CLOCKSTATIST) /* conditional if clause for conditional syslog */
{
msyslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
CLK_UNIT(parse->peer),
l_mktime(current_time - parse->generic->timestarted));
msyslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
CLK_UNIT(parse->peer),
clockstatus(parse->generic->currentstatus));
for (i = 0; i <= CEVNT_MAX; i++)
{
u_long stime;
u_long percent, div = current_time - parse->generic->timestarted;
percent = stime = PARSE_STATETIME(parse, i);
while (((u_long)(~0) / 10000) < percent)
{
percent /= 10;
div /= 10;
}
if (div)
percent = (percent * 10000) / div;
else
percent = 10000;
if (stime)
msyslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3d.%02d%%)",
CLK_UNIT(parse->peer),
clockstatus(i),
l_mktime(stime),
percent / 100, percent % 100);
}
}
}
/*--------------------------------------------------
* cparse_statistics - wrapper for statistics call
*/
static void
cparse_statistics(peer)
struct peer *peer;
{
struct parseunit *parse = (struct parseunit *)peer;
parse_statistics(parse);
parse->stattimer.event_time = current_time + PARSESTATISTICS;
TIMER_ENQUEUE(timerqueue, &parse->stattimer);
}
/**===========================================================================
** xntp interface routines
**/
/*--------------------------------------------------
* parse_init - initialize internal parse driver data
*/
static void
parse_init()
{
memset((caddr_t)parseunits, 0, sizeof parseunits);
}
/*--------------------------------------------------
* parse_shutdown - shut down a PARSE clock
*/
static void
parse_shutdown(unit, peer)
int unit;
struct peer *peer;
{
struct parseunit *parse;
unit = CLK_UNIT(peer);
if (unit >= MAXUNITS)
{
msyslog(LOG_ERR,
"PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit invalid (max %d)",
unit,MAXUNITS);
return;
}
parse = parseunits[unit];
if (parse && !parse->peer)
{
msyslog(LOG_ERR,
"PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit not in use", unit);
return;
}
/*
* print statistics a last time and
* stop statistics machine
*/
parse_statistics(parse);
TIMER_DEQUEUE(&parse->stattimer);
#if ATOM
{
/*
* kill possible PPS association
*/
if (fdpps == parse->generic->io.fd)
fdpps = -1;
}
#endif
if (parse->parse_type->cl_end)
{
parse->parse_type->cl_end(parse);
}
if (parse->binding)
PARSE_END(parse);
/*
* Tell the I/O module to turn us off. We're history.
*/
if (!parse->pollonly)
io_closeclock(&parse->generic->io);
else
(void) close(parse->generic->io.fd);
NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
CLK_UNIT(parse->peer), parse->parse_type->cl_description);
parse->peer = (struct peer *)0; /* unused now */
}
/*--------------------------------------------------
* parse_start - open the PARSE devices and initialize data for processing
*/
static int
parse_start(sysunit, peer)
int sysunit;
struct peer *peer;
{
u_int unit;
int fd232, i;
#ifdef HAVE_TERMIOS
struct termios tio; /* NEEDED FOR A LONG TIME ! */
#endif
#ifdef HAVE_SYSV_TTYS
struct termio tio; /* NEEDED FOR A LONG TIME ! */
#endif
struct parseunit * parse;
char parsedev[sizeof(PARSEDEVICE)+20];
parsectl_t tmp_ctl;
u_int type;
type = CLK_TYPE(peer);
unit = CLK_UNIT(peer);
if (unit >= MAXUNITS)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit number invalid (max %d)",
unit, MAXUNITS-1);
return 0;
}
if ((type == ~0) || (parse_clockinfo[type].cl_description == (char *)0))
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
unit, CLK_REALTYPE(peer), ncltypes-1);
return 0;
}
if (parseunits[unit] && parseunits[unit]->peer)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit in use", unit);
return 0;
}
/*
* Unit okay, attempt to open the device.
*/
(void) sprintf(parsedev, PARSEDEVICE, unit);
#ifndef O_NOCTTY
#define O_NOCTTY 0
#endif
fd232 = open(parsedev, O_RDWR | O_NOCTTY
#ifdef O_NONBLOCK
| O_NONBLOCK
#endif
, 0777);
if (fd232 == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
return 0;
}
/*
* Looks like this might succeed. Find memory for the structure.
* Look to see if there are any unused ones, if not we malloc()
* one.
*/
if (parseunits[unit])
{
parse = parseunits[unit]; /* The one we want is okay - and free */
}
else
{
for (i = 0; i < MAXUNITS; i++)
{
if (parseunits[i] && !parseunits[i]->peer)
break;
}
if (i < MAXUNITS)
{
/*
* Reclaim this one
*/
parse = parseunits[i];
parseunits[i] = (struct parseunit *)0;
}
else
{
parse = (struct parseunit *)
emalloc(sizeof(struct parseunit));
}
}
memset((char *)parse, 0, sizeof(struct parseunit));
parseunits[unit] = parse;
parse->generic = peer->procptr;
/*
* Set up the structures
*/
parse->generic->timestarted = current_time;
parse->lastchange = current_time;
/*
* we want to filter input for the sake of
* getting an impression on dispersion
* also we like to average the median range
*/
parse->generic->currentstatus = CEVNT_TIMEOUT; /* expect the worst */
parse->flags = 0;
parse->pollneeddata = 0;
parse->pollonly = 1; /* go for default polling mode */
parse->lastformat = ~0; /* assume no format known */
parse->laststatus = ~0; /* be sure to mark initial status change */
parse->lastmissed = 0; /* assume got everything */
parse->ppsserial = 0;
parse->localdata = (void *)0;
clear_err(parse, ERR_ALL);
parse->parse_type = &parse_clockinfo[type];
parse->generic->fudgetime1.l_ui = 0; /* we can only pre-configure delays less than 1 second */
parse->generic->fudgetime1.l_uf = parse->parse_type->cl_basedelay;
parse->generic->fudgetime2.l_ui = 0; /* we can only pre-configure delays less than 1 second */
parse->generic->fudgetime2.l_uf = parse->parse_type->cl_ppsdelay;
parse->generic->clockdesc = parse->parse_type->cl_description;
peer->rootdelay = parse->parse_type->cl_rootdelay;
peer->sstclktype = parse->parse_type->cl_type;
peer->precision = sys_precision;
peer->stratum = STRATUM_REFCLOCK;
if (peer->stratum <= 1)
memmove((char *)&parse->generic->refid, parse->parse_type->cl_id, 4);
else
parse->generic->refid = htonl(PARSEHSREFID);
parse->generic->io.fd = fd232;
parse->peer = peer; /* marks it also as busy */
parse->binding = init_iobinding(parse);
if (parse->binding == (bind_t *)0)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.");
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0; /* well, ok - special initialisation broke */
}
/*
* configure terminal line
*/
if (TTY_GETATTR(fd232, &tio) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tio): %m", unit, fd232);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0;
}
else
{
#ifndef _PC_VDISABLE
memset((char *)tio.c_cc, 0, sizeof(tio.c_cc));
#else
int disablec;
errno = 0; /* pathconf can deliver -1 without changing errno ! */
disablec = fpathconf(parse->generic->io.fd, _PC_VDISABLE);
if (disablec == -1 && errno)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CLK_UNIT(parse->peer));
memset((char *)tio.c_cc, 0, sizeof(tio.c_cc)); /* best guess */
}
else
if (disablec != -1)
memset((char *)tio.c_cc, disablec, sizeof(tio.c_cc));
#endif
#if defined (VMIN) || defined(VTIME)
if ((parse_clockinfo[type].cl_lflag & ICANON) == 0)
{
#ifdef VMIN
tio.c_cc[VMIN] = 1;
#endif
#ifdef VTIME
tio.c_cc[VTIME] = 0;
#endif
}
#endif
tio.c_cflag = parse_clockinfo[type].cl_cflag;
tio.c_iflag = parse_clockinfo[type].cl_iflag;
tio.c_oflag = parse_clockinfo[type].cl_oflag;
tio.c_lflag = parse_clockinfo[type].cl_lflag;
#ifdef HAVE_TERMIOS
if ((cfsetospeed(&tio, parse_clockinfo[type].cl_speed) == -1) ||
(cfsetispeed(&tio, parse_clockinfo[type].cl_speed) == -1))
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcset{i,o}speed(&tio, speed): %m", unit);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0;
}
#else
tio.c_cflag |= parse_clockinfo[type].cl_speed;
#endif
if (TTY_SETATTR(fd232, &tio) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tio): %m", unit, fd232);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0;
}
}
/*
* as we always(?) get 8 bit chars we want to be
* sure, that the upper bits are zero for less
* than 8 bit I/O - so we pass that information on.
* note that there can be only one bit count format
* per file descriptor
*/
switch (tio.c_cflag & CSIZE)
{
case CS5:
tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
break;
case CS6:
tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
break;
case CS7:
tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
break;
case CS8:
tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
break;
}
if (!PARSE_SETCS(parse, &tmp_ctl))
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0; /* well, ok - special initialisation broke */
}
strcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format);
tmp_ctl.parseformat.parse_count = strlen(tmp_ctl.parseformat.parse_buffer);
if (!PARSE_SETFMT(parse, &tmp_ctl))
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0; /* well, ok - special initialisation broke */
}
/*
* get rid of all IO accumulated so far
*/
#ifdef HAVE_TERMIOS
(void) tcflush(parse->generic->io.fd, TCIOFLUSH);
#else
#ifdef TCFLSH
{
#ifndef TCIOFLUSH
#define TCIOFLUSH 2
#endif
int flshcmd = TCIOFLUSH;
(void) ioctl(parse->generic->io.fd, TCFLSH, (caddr_t)&flshcmd);
}
#endif
#endif
/*
* try to do any special initializations
*/
if (parse->parse_type->cl_init)
{
if (parse->parse_type->cl_init(parse))
{
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0; /* well, ok - special initialisation broke */
}
}
if (!(parse->parse_type->cl_flags & PARSE_F_POLLONLY) &&
(CLK_PPS(parse->peer) || (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)))
{
/*
* Insert in async io device list.
*/
parse->generic->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
parse->generic->io.srcclock = (caddr_t)parse;
parse->generic->io.datalen = 0;
if (!io_addclock(&parse->generic->io))
{
if (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)
{
msyslog(LOG_ERR,
"PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CLK_UNIT(parse->peer), parsedev);
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0;
}
else
{
msyslog(LOG_ERR,
"PARSE receiver #%d: parse_start: addclock %s fails (switching to polling mode)", CLK_UNIT(parse->peer), parsedev);
}
}
else
{
parse->pollonly = 0; /*
* update at receipt of time_stamp - also
* supports PPS processing
*/
}
}
#ifdef ATOM
if (parse->pollonly && (parse->parse_type->cl_flags & PARSE_F_PPSPPS))
{
if (fdpps == -1)
{
fdpps = parse->generic->io.fd;
if (!PARSE_DISABLE(parse))
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_disable() FAILED", CLK_UNIT(parse->peer));
parse_shutdown(CLK_UNIT(parse->peer), peer); /* let our cleaning staff do the work */
return 0;
}
}
else
{
msyslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: loopfilter PPS already active - no PPS via CIOGETEV", CLK_UNIT(parse->peer));
}
}
#endif
/*
* wind up statistics timer
*/
parse->stattimer.peer = (struct peer *)parse; /* we know better, but what the heck */
parse->stattimer.event_handler = cparse_statistics;
parse->stattimer.event_time = current_time + PARSESTATISTICS;
TIMER_ENQUEUE(timerqueue, &parse->stattimer);
/*
* get out Copyright information once
*/
if (!notice)
{
NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
msyslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1996, Frank Kardel");
notice = 1;
}
/*
* print out configuration
*/
NLOG(NLOG_CLOCKINFO)
{
/* conditional if clause for conditional syslog */
msyslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added",
CLK_UNIT(parse->peer),
parse->parse_type->cl_description, parsedev);
msyslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d",
CLK_UNIT(parse->peer),
parse->peer->stratum, (parse->pollonly || !CLK_PPS(parse->peer)) ? "no " : "",
l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision);
msyslog(LOG_INFO, "PARSE receiver #%d: rootdelay %s s, phaseadjust %s s, %s IO handling",
CLK_UNIT(parse->peer),
ufptoa(parse->parse_type->cl_rootdelay, 6),
lfptoa(&parse->generic->fudgetime1, 8),
parse->binding->bd_description);
msyslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CLK_UNIT(parse->peer),
!(*parse->parse_type->cl_format) ? "<AUTOMATIC>" : parse->parse_type->cl_format);
#ifdef ATOM
msyslog(LOG_INFO, "PARSE receiver #%d: %sCD PPS support",
CLK_UNIT(parse->peer),
(fdpps == parse->generic->io.fd) ? "" : "NO ");
#endif
}
return 1;
}
/*--------------------------------------------------
* parse_poll - called by the transmit procedure
*/
static void
parse_poll(unit, peer)
int unit;
struct peer *peer;
{
struct parseunit *parse;
unit = CLK_UNIT(peer);
if (unit >= MAXUNITS)
{
msyslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit invalid",
unit);
return;
}
parse = parseunits[unit];
if (!parse->peer)
{
msyslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit unused",
unit);
return;
}
if (peer != parse->peer)
{
msyslog(LOG_ERR,
"PARSE receiver #%d: poll: INTERNAL: peer incorrect",
unit);
return;
}
/*
* Update clock stat counters
*/
parse->generic->polls++;
/*
* in PPS mode we just mark that we want the next sample
* for the clock filter
*/
if (!parse->pollonly)
{
if (parse->pollneeddata)
{
/*
* bad news - didn't get a response last time
*/
parse->generic->noreply++;
parse->lastmissed = current_time;
parse_event(parse, CEVNT_TIMEOUT);
ERR(ERR_NODATA)
msyslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval (check receiver / cableling)", CLK_UNIT(parse->peer));
}
parse->pollneeddata = 1;
if (parse->parse_type->cl_poll)
{
parse->parse_type->cl_poll(parse);
}
return;
}
/*
* the following code is only executed only when polling is used
*/
PARSE_POLL(parse);
}
#define LEN_STATES 300 /* length of state string */
/*--------------------------------------------------
* parse_control - set fudge factors, return statistics
*/
static void
parse_control(unit, in, out)
int unit;
struct refclockstat *in;
struct refclockstat *out;
{
struct parseunit *parse;
parsectl_t tmpctl;
u_long type;
static char outstatus[400]; /* status output buffer */
char *start;
if (out)
{
out->lencode = 0;
out->p_lastcode = 0;
out->kv_list = (struct ctl_var *)0;
}
if (unit >= MAXUNITS)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (max %d)",
unit, MAXUNITS-1);
return;
}
/*
* XXX - UGLY - unit needs to be eliminated in favor of a peer *
*/
parse = parseunits[unit];
if (!parse || !parse->peer)
{
msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
unit);
return;
}
type = CLK_TYPE(parse->peer);
unit = CLK_UNIT(parse->peer);
if (in)
{
if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
{
parse->flags = in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4);
}
}
if (out)
{
u_long sum = 0;
char *t, *tt;
struct tm *tm;
short utcoff;
char sign;
int i;
time_t tim;
outstatus[0] = '\0';
out->type = REFCLK_PARSE;
out->haveflags |= CLK_HAVETIME2;
/*
* figure out skew between PPS and RS232 - just for informational
* purposes - returned in time2 value
*/
if (PARSE_SYNC(parse->time.parse_state))
{
if (PARSE_PPS(parse->time.parse_state) && PARSE_TIMECODE(parse->time.parse_state))
{
l_fp off;
/*
* we have a PPS and RS232 signal - calculate the skew
* WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
*/
off = parse->time.parse_stime.fp;
L_SUB(&off, &parse->time.parse_ptime.fp); /* true offset */
tt = add_var(&out->kv_list, 80, RO);
sprintf(tt, "refclock_ppsskew=%s", lfptoms(&off, 6));
}
}
if (PARSE_PPS(parse->time.parse_state))
{
tt = add_var(&out->kv_list, 80, RO|DEF);
sprintf(tt, "refclock_ppstime=\"%s\"", prettydate(&parse->time.parse_ptime.fp));
}
/*
* all this for just finding out the +-xxxx part (there are always
* new and changing fields in the standards 8-().
*
* but we do it for the human user...
*/
tim = parse->time.parse_time.fp.l_ui - JAN_1970;
tm = gmtime(&tim);
utcoff = tm->tm_hour * 60 + tm->tm_min;
tm = localtime(&tim);
utcoff = tm->tm_hour * 60 + tm->tm_min - utcoff + 12 * 60;
utcoff += 24 * 60;
utcoff %= 24 * 60;
utcoff -= 12 * 60;
if (utcoff < 0)
{
utcoff = -utcoff;
sign = '-';
}
else
{
sign = '+';
}
tt = add_var(&out->kv_list, 128, RO|DEF);
sprintf(tt, "refclock_time=\"");
tt += strlen(tt);
if (parse->time.parse_time.fp.l_ui == 0)
{
strcpy(tt, "<UNDEFINED>\"");
}
else
{
strcpy(tt, prettydate(&parse->time.parse_time.fp));
t = tt + strlen(tt);
sprintf(t, " (%c%02d%02d)\"", sign, utcoff / 60, utcoff % 60);
}
if (!PARSE_GETTIMECODE(parse, &tmpctl))
{
ERR(ERR_INTERNAL)
msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
}
else
{
tt = add_var(&out->kv_list, 512, RO|DEF);
sprintf(tt, "refclock_status=\"");
tt += strlen(tt);
/*
* copy PPS flags from last read transaction (informational only)
*/
tmpctl.parsegettc.parse_state |= parse->time.parse_state &
(PARSEB_PPS|PARSEB_S_PPS);
(void) parsestate(tmpctl.parsegettc.parse_state, tt);
strcat(tt, "\"");
if (tmpctl.parsegettc.parse_count)
mkascii(outstatus+strlen(outstatus), sizeof(outstatus)- strlen(outstatus) - 1,
tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1);
parse->generic->badformat += tmpctl.parsegettc.parse_badformat;
}
tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
if (!PARSE_GETFMT(parse, &tmpctl))
{
ERR(ERR_INTERNAL)
msyslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
}
else
{
tt = add_var(&out->kv_list, 80, RO|DEF);
sprintf(tt, "refclock_format=\"");
strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count);
strcat(tt,"\"");
}
/*
* gather state statistics
*/
start = tt = add_var(&out->kv_list, LEN_STATES, RO|DEF);
strcpy(tt, "refclock_states=\"");
tt += strlen(tt);
for (i = 0; i <= CEVNT_MAX; i++)
{
u_long stime;
u_long div = current_time - parse->generic->timestarted;
u_long percent;
percent = stime = PARSE_STATETIME(parse, i);
while (((u_long)(~0) / 10000) < percent)
{
percent /= 10;
div /= 10;
}
if (div)
percent = (percent * 10000) / div;
else
percent = 10000;
if (stime)
{
char item[80];
int count;
sprintf(item, "%s%s%s: %s (%d.%02d%%)",
sum ? "; " : "",
(parse->generic->currentstatus == i) ? "*" : "",
clockstatus(i),
l_mktime(stime),
(int)(percent / 100), (int)(percent % 100));
if ((count = strlen(item)) < (LEN_STATES - 40 - (tt - start)))
{
strcpy(tt, item);
tt += count;
}
sum += stime;
}
}
sprintf(tt, "; running time: %s\"", l_mktime(sum));
tt = add_var(&out->kv_list, 32, RO);
sprintf(tt, "refclock_id=\"%s\"", parse->parse_type->cl_id);
tt = add_var(&out->kv_list, 80, RO);
sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description);
tt = add_var(&out->kv_list, 128, RO);
sprintf(tt, "refclock_driver_version=\"refclock_parse.c,v 3.103 1997/07/12 15:35:16 kardel Exp\"");
out->lencode = strlen(outstatus);
out->p_lastcode = outstatus;
}
}
/**===========================================================================
** processing routines
**/
/*--------------------------------------------------
* event handling - note that nominal events will also be posted
*/
static void
parse_event(parse, event)
struct parseunit *parse;
int event;
{
if (parse->generic->currentstatus != (u_char) event)
{
parse->statetime[parse->generic->currentstatus] += current_time - parse->lastchange;
parse->lastchange = current_time;
parse->generic->currentstatus = (u_char)event;
if (parse->parse_type->cl_event)
parse->parse_type->cl_event(parse, event);
if (event != CEVNT_NOMINAL)
{
parse->generic->lastevent = parse->generic->currentstatus;
}
else
{
NLOG(NLOG_CLOCKSTATUS)
msyslog(LOG_INFO, "PARSE receiver #%d: SYNCHRONIZED",
CLK_UNIT(parse->peer));
}
if (event == CEVNT_FAULT)
{
NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */
ERR(ERR_BADEVENT)
msyslog(LOG_ERR,
"clock %s fault '%s' (0x%02x)", refnumtoa(parse->peer->srcadr.sin_addr.s_addr), ceventstr(event),
(u_int)event);
}
else
{
NLOG(NLOG_CLOCKEVENT) /* conditional if clause for conditional syslog */
if (event == CEVNT_NOMINAL || list_err(parse, ERR_BADEVENT))
msyslog(LOG_INFO,
"clock %s event '%s' (0x%02x)", refnumtoa(parse->peer->srcadr.sin_addr.s_addr), ceventstr(event),
(u_int)event);
}
report_event(EVNT_PEERCLOCK, parse->peer);
report_event(EVNT_CLOCKEXCPT, parse->peer);
}
}
/*--------------------------------------------------
* process a PARSE time sample
*/
static void
parse_process(parse, parsetime)
struct parseunit *parse;
parsetime_t *parsetime;
{
unsigned char leap;
l_fp off, rectime, reftime;
/*
* check for changes in conversion status
* (only one for each new status !)
*/
if (parse->laststatus != parsetime->parse_status)
{
char buffer[400];
NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
msyslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
CLK_UNIT(parse->peer), parsestatus(parsetime->parse_status, buffer));
if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
{
/*
* tell more about the story - list time code
* there is a slight change for a race condition and
* the time code might be overwritten by the next packet
*/
parsectl_t tmpctl;
if (!PARSE_GETTIMECODE(parse, &tmpctl))
{
ERR(ERR_INTERNAL)
msyslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CLK_UNIT(parse->peer));
}
else
{
ERR(ERR_BADDATA)
msyslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\" (check receiver configuration / cableling)",
CLK_UNIT(parse->peer), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1));
parse->generic->badformat += tmpctl.parsegettc.parse_badformat;
}
}
parse->laststatus = parsetime->parse_status;
}
/*
* examine status and post appropriate events
*/
if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
{
/*
* got bad data - tell the rest of the system
*/
switch (parsetime->parse_status & CVT_MASK)
{
case CVT_NONE:
break; /* well, still waiting - timeout is handled at higher levels */
case CVT_FAIL:
parse->generic->badformat++;
if (parsetime->parse_status & CVT_BADFMT)
{
parse_event(parse, CEVNT_BADREPLY);
}
else
if (parsetime->parse_status & CVT_BADDATE)
{
parse_event(parse, CEVNT_BADDATE);
}
else
if (parsetime->parse_status & CVT_BADTIME)
{
parse_event(parse, CEVNT_BADTIME);
}
else
{
parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
}
}
return; /* skip the rest - useless */
}
/*
* check for format changes
* (in case somebody has swapped clocks 8-)
*/
if (parse->lastformat != parsetime->parse_format)
{
parsectl_t tmpctl;
tmpctl.parseformat.parse_format = parsetime->parse_format;
if (!PARSE_GETFMT(parse, &tmpctl))
{
ERR(ERR_INTERNAL)
msyslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CLK_UNIT(parse->peer));
}
else
{
NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
msyslog(LOG_INFO, "PARSE receiver #%d: new packet format \"%s\"",
CLK_UNIT(parse->peer), tmpctl.parseformat.parse_buffer);
}
parse->lastformat = parsetime->parse_format;
}
/*
* now, any changes ?
*/
if (parse->time.parse_state != parsetime->parse_state)
{
char tmp1[200];
char tmp2[200];
/*
* something happend
*/
(void) parsestate(parsetime->parse_state, tmp1);
(void) parsestate(parse->time.parse_state, tmp2);
NLOG(NLOG_CLOCKINFO) /* conditional if clause for conditional syslog */
msyslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
CLK_UNIT(parse->peer), tmp2, tmp1);
}
/*
* remember for future
*/
parse->time = *parsetime;
/*
* check to see, whether the clock did a complete powerup or lost PZF signal
* and post correct events for current condition
*/
if (PARSE_POWERUP(parsetime->parse_state))
{
/*
* this is bad, as we have completely lost synchronisation
* well this is a problem with the receiver here
* for PARSE Meinberg DCF77 receivers the lost synchronisation
* is true as it is the powerup state and the time is taken
* from a crude real time clock chip
* for the PZF series this is only partly true, as
* PARSE_POWERUP only means that the pseudo random
* phase shift sequence cannot be found. this is only
* bad, if we have never seen the clock in the SYNC
* state, where the PHASE and EPOCH are correct.
* for reporting events the above business does not
* really matter, but we can use the time code
* even in the POWERUP state after having seen
* the clock in the synchronized state (PZF class
* receivers) unless we have had a telegram disruption
* after having seen the clock in the SYNC state. we
* thus require having seen the clock in SYNC state
* *after* having missed telegrams (noresponse) from
* the clock. one problem remains: we might use erroneously
* POWERUP data if the disruption is shorter than 1 polling
* interval. fortunately powerdowns last usually longer than 64
* seconds and the receiver is at least 2 minutes in the
* POWERUP or NOSYNC state before switching to SYNC
*/
parse_event(parse, CEVNT_FAULT);
NLOG(NLOG_CLOCKSTATUS)
ERR(ERR_BADSTATUS)
msyslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED",
CLK_UNIT(parse->peer));
}
else
{
/*
* we have two states left
*
* SYNC:
* this state means that the EPOCH (timecode) and PHASE
* information has be read correctly (at least two
* successive PARSE timecodes were received correctly)
* this is the best possible state - full trust
*
* NOSYNC:
* The clock should be on phase with respect to the second
* signal, but the timecode has not been received correctly within
* at least the last two minutes. this is a sort of half baked state
* for PARSE Meinberg DCF77 clocks this is bad news (clock running
* without timecode confirmation)
* PZF 535 has also no time confirmation, but the phase should be
* very precise as the PZF signal can be decoded
*/
if (PARSE_SYNC(parsetime->parse_state))
{
/*
* currently completely synchronized - best possible state
*/
parse->lastsync = current_time;
clear_err(parse, ERR_BADSTATUS);
}
else
{
/*
* we have had some problems receiving the time code
*/
parse_event(parse, CEVNT_PROP);
NLOG(NLOG_CLOCKSTATUS)
ERR(ERR_BADSTATUS)
msyslog(LOG_ERR,"PARSE receiver #%d: TIMECODE NOT CONFIRMED",
CLK_UNIT(parse->peer));
}
}
if (PARSE_TIMECODE(parsetime->parse_state))
{
l_fp offset;
/*
* calculate time offset including systematic delays
* off = PARSE-timestamp + propagation delay - kernel time stamp
*/
offset = parse->generic->fudgetime1;
off = parsetime->parse_time.fp;
reftime = off;
L_ADD(&off, &offset);
rectime = off; /* this makes org time and xmt time somewhat artificial */
L_SUB(&off, &parsetime->parse_stime.fp);
}
if (PARSE_PPS(parsetime->parse_state) && CLK_PPS(parse->peer))
{
l_fp offset;
/*
* we have a PPS signal - much better than the RS232 stuff (we hope)
*/
offset = parsetime->parse_ptime.fp;
L_ADD(&offset, &parse->generic->fudgetime2);
if (PARSE_TIMECODE(parsetime->parse_state))
{
if (M_ISGEQ(off.l_i, off.l_f, -1, 0x80000000) &&
M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_f))
{
/*
* RS232 offsets within [-0.5..0.5[ - take PPS offsets
*/
if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
{
reftime = off = offset;
rectime = offset;
/*
* implied on second offset
*/
off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
}
else
{
/*
* time code describes pulse
*/
off = parsetime->parse_time.fp;
rectime = reftime = off; /* take reference time - fake rectime */
L_SUB(&off, &offset); /* true offset */
}
}
/*
* take RS232 offset when PPS when out of bounds
*/
}
else
{
/*
* Well, no time code to guide us - assume on second pulse
* and pray, that we are within [-0.5..0.5[
*/
reftime = off = offset;
rectime = offset;
/*
* implied on second offset
*/
off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
}
}
else
{
if (!PARSE_TIMECODE(parsetime->parse_state))
{
/*
* Well, no PPS, no TIMECODE, no more work ...
*/
return;
}
}
parse->generic->lasttime = current_time;
if (!refclock_sample(&off, parse->generic, parse->parse_type->cl_samples, parse->parse_type->cl_keep))
{
parse_event(parse, CEVNT_BADTIME);
return;
}
#if defined(ATOM)
if (CLK_PPS(parse->peer) && !parse->pollonly && PARSE_SYNC(parsetime->parse_state))
{
/*
* only provide PPS information when clock
* is in sync
* thus PHASE and EPOCH are correct and PPS is not
* done via the CIOGETEV loopfilter mechanism
*/
if (fdpps != parse->generic->io.fd)
(void) pps_sample(&parse->generic->offset);
}
#endif /* ATOM */
/*
* ready, unless the machine wants a sample
*/
if (!parse->pollonly && !parse->pollneeddata)
return;
if (PARSE_SYNC(parsetime->parse_state))
{
/*
* log OK status
*/
parse_event(parse, CEVNT_NOMINAL);
}
parse->pollneeddata = 0;
clear_err(parse, ERR_BADIO);
clear_err(parse, ERR_BADDATA);
clear_err(parse, ERR_NODATA);
clear_err(parse, ERR_INTERNAL);
parse->generic->lastrec = rectime;
parse->generic->lastref = reftime;
/*
* and now stick it into the clock machine
* samples are only valid iff lastsync is not too old and
* we have seen the clock in sync at least once
* after the last time we didn't see an expected data telegram
* see the clock states section above for more reasoning
*/
if (((current_time - parse->lastsync) > parse->parse_type->cl_maxunsync) ||
(parse->lastsync <= parse->lastmissed))
{
leap = LEAP_NOTINSYNC;
}
else
{
if (PARSE_LEAPADD(parsetime->parse_state))
{
/*
* we pick this state also for time code that pass leap warnings
* without direction information (as earth is currently slowing
* down).
*/
leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
}
else
if (PARSE_LEAPDEL(parsetime->parse_state))
{
leap = LEAP_DELSECOND;
}
else
{
leap = LEAP_NOWARNING;
}
}
refclock_receive(parse->peer, &parse->generic->offset, 0,
parse->generic->dispersion, &reftime, &rectime,
leap | REFCLOCK_OWN_STATES);
}
/**===========================================================================
** clock polling support
**/
struct poll_timer
{
struct event timer; /* we'd like to poll a a higher rate than 1/64s */
};
typedef struct poll_timer poll_timer_t;
/*--------------------------------------------------
* direct poll routine
*/
static void
poll_dpoll(parse)
struct parseunit *parse;
{
int rtc;
char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
rtc = write(parse->generic->io.fd, ps, ct);
if (rtc < 0)
{
ERR(ERR_BADIO)
msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
}
else
if (rtc != ct)
{
ERR(ERR_BADIO)
msyslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CLK_UNIT(parse->peer), rtc, ct);
}
clear_err(parse, ERR_BADIO);
}
/*--------------------------------------------------
* periodic poll routine
*/
static void
poll_poll(parse)
struct parseunit *parse;
{
poll_timer_t *pt = (poll_timer_t *)parse->localdata;
poll_dpoll(parse);
if (pt != (poll_timer_t *)0)
{
pt->timer.event_time = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
TIMER_ENQUEUE(timerqueue, &pt->timer);
}
}
/*--------------------------------------------------
* init routine - setup timer
*/
static int
poll_init(parse)
struct parseunit *parse;
{
poll_timer_t *pt;
if (((poll_info_t *)parse->parse_type->cl_data)->rate)
{
parse->localdata = (void *)emalloc(sizeof(poll_timer_t));
memset((char *)parse->localdata, 0, sizeof(poll_timer_t));
pt = (poll_timer_t *)parse->localdata;
pt->timer.peer = (struct peer *)parse; /* well, only we know what it is */
pt->timer.event_handler = (void (*) P((struct peer *))) poll_poll;
poll_poll(parse);
}
else
{
parse->localdata = (void *)0;
}
return 0;
}
/*--------------------------------------------------
* end routine - clean up timer
*/
static void
poll_end(parse)
struct parseunit *parse;
{
if (parse->localdata != (void *)0)
{
TIMER_DEQUEUE(&((poll_timer_t *)parse->localdata)->timer);
free((char *)parse->localdata);
parse->localdata = (void *)0;
}
}
/**===========================================================================
** special code for special clocks
**/
/*--------------------------------------------------
* trimble TAIP init routine - setup EOL and then do poll_init.
*/
static int
trimbletaip_init(parse)
struct parseunit *parse;
{
#ifdef HAVE_TERMIOS
struct termios tio;
#endif
#ifdef HAVE_SYSV_TTYS
struct termio tio;
#endif
/*
* configure terminal line for trimble receiver
*/
if (TTY_GETATTR(parse->generic->io.fd, &tio) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
return 0;
}
else
{
tio.c_cc[VEOL] = TRIMBLETAIP_EOL;
if (TTY_SETATTR(parse->generic->io.fd, &tio) == -1)
{
msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tio): %m", CLK_UNIT(parse->peer));
return 0;
}
}
return poll_init(parse);
}
/*--------------------------------------------------
* trimble TAIP event routine - reset receiver upon data format trouble
*/
static char *taipinit[] = {
">FPV00000000<",
">SRM;ID_FLAG=F;CS_FLAG=T;EC_FLAG=F;FR_FLAG=T;CR_FLAG=F<",
">FTM00020001<",
(char *)0
};
static void
trimbletaip_event(parse, event)
struct parseunit *parse;
int event;
{
switch (event)
{
case CEVNT_BADREPLY: /* reset on garbled input */
case CEVNT_TIMEOUT: /* reset on no input */
{
char **iv;
iv = taipinit;
while (*iv)
{
int rtc = write(parse->generic->io.fd, *iv, strlen(*iv));
if (rtc < 0)
{
msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
return;
}
else
{
if (rtc != strlen(*iv))
{
msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: failed to send cmd incomplete (%d of %d bytes sent)",
CLK_UNIT(parse->peer), rtc, strlen(*iv));
return;
}
}
iv++;
}
NLOG(NLOG_CLOCKINFO)
ERR(ERR_BADIO)
msyslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_event: RECEIVER INITIALIZED",
CLK_UNIT(parse->peer));
}
break;
default: /* ignore */
break;
}
}
/*
* This driver supports the Trimble SVee Six Plus GPS receiver module.
* It should support other Trimble receivers which use the Trimble Standard
* Interface Protocol (see below).
*
* The module has a serial I/O port for command/data and a 1 pulse-per-second
* output, about 1 microsecond wide. The leading edge of the pulse is
* coincident with the change of the GPS second. This is the same as
* the change of the UTC second +/- ~1 microsecond. Some other clocks
* specifically use a feature in the data message as a timing reference, but
* the SVee Six Plus does not do this. In fact there is considerable jitter
* on the timing of the messages, so this driver only supports the use
* of the PPS pulse for accurate timing. Where it is determined that
* the offset is way off, when first starting up xntpd for example,
* the timing of the data stream is used until the offset becomes low enough
* (|offset| < CLOCK_MAX), at which point the pps offset is used.
*
* It can use either option for receiving PPS information - the 'ppsclock'
* stream pushed onto the serial data interface to timestamp the Carrier
* Detect interrupts, where the 1PPS connects to the CD line. This only
* works on SunOS 4.1.x currently. To select this, define PPSPPS in
* Config.local. The other option is to use a pulse-stretcher/level-converter
* to convert the PPS pulse into a RS232 start pulse & feed this into another
* tty port. To use this option, define PPSCLK in Config.local. The pps input,
* by whichever method, is handled in ntp_loopfilter.c
*
* The receiver uses a serial message protocol called Trimble Standard
* Interface Protocol (it can support others but this driver only supports
* TSIP). Messages in this protocol have the following form:
*
* <DLE><id> ... <data> ... <DLE><ETX>
*
* Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
* on transmission and compressed back to one on reception. Otherwise
* the values of data bytes can be anything. The serial interface is RS-422
* asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
* in total!), and 1 stop bit. The protocol supports byte, integer, single,
* and double datatypes. Integers are two bytes, sent most significant first.
* Singles are IEEE754 single precision floating point numbers (4 byte) sent
* sign & exponent first. Doubles are IEEE754 double precision floating point
* numbers (8 byte) sent sign & exponent first.
* The receiver supports a large set of messages, only a small subset of
* which are used here. From driver to receiver the following are used:
*
* ID Description
*
* 21 Request current time
* 22 Mode Select
* 2C Set/Request operating parameters
* 2F Request UTC info
* 35 Set/Request I/O options
* From receiver to driver the following are recognised:
*
* ID Description
*
* 41 GPS Time
* 44 Satellite selection, PDOP, mode
* 46 Receiver health
* 4B Machine code/status
* 4C Report operating parameters (debug only)
* 4F UTC correction data (used to get leap second warnings)
* 55 I/O options (debug only)
*
* All others are accepted but ignored.
*
*/
#define PI 3.1415926535898 /* lots of sig figs */
#define D2R PI/180.0
/*-------------------------------------------------------------------
* sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
* interface to the receiver.
*
* CAVEAT: the sendflt, sendint routines are byte order dependend and
* float implementation dependend - these must be converted to portable
* versions !
*/
union {
u_char bd[8];
int iv;
float fv;
double dv;
} uval;
struct txbuf
{
short idx; /* index to first unused byte */
u_char *txt; /* pointer to actual data buffer */
};
static void sendcmd P((struct txbuf *, u_char));
static void sendbyte P((struct txbuf *, u_char));
static void sendetx P((struct txbuf *, struct parseunit *));
#if 0
static void sendint P((struct txbuf *, int));
#endif
static void sendflt P((struct txbuf *, double));
static void
sendcmd(buf, c)
struct txbuf *buf;
u_char c;
{
buf->txt[0] = DLE;
buf->txt[1] = c;
buf->idx = 2;
}
static void
sendbyte(buf, b)
struct txbuf *buf;
u_char b;
{
if (b == DLE)
buf->txt[buf->idx++] = DLE;
buf->txt[buf->idx++] = b;
}
static void
sendetx(buf, parse)
struct txbuf *buf;
struct parseunit *parse;
{
buf->txt[buf->idx++] = DLE;
buf->txt[buf->idx++] = ETX;
if (write(parse->generic->io.fd, buf->txt, buf->idx) != buf->idx)
{
ERR(ERR_BADIO)
msyslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CLK_UNIT(parse->peer));
}
else
{
clear_err(parse, ERR_BADIO);
}
}
#if 0
static void
sendint(buf, a)
struct txbuf *buf;
int a;
{
/* send 16bit int, msbyte first */
sendbyte(buf, (a>>8) & 0xff);
sendbyte(buf, a & 0xff);
}
#endif
static void
sendflt(buf, a)
struct txbuf *buf;
float a;
{
int i;
uval.fv = a;
#ifdef XNTP_BIG_ENDIAN
for (i=0; i<=3; i++)
#else
for (i=3; i>=0; i--)
#endif
sendbyte(buf, uval.bd[i]);
}
/*--------------------------------------------------
* trimble TSIP init routine
*/
static int
trimbletsip_init(parse)
struct parseunit *parse;
{
u_char buffer[256];
struct txbuf buf;
buf.txt = buffer;
if (!poll_init(parse))
{
sendcmd(&buf, 0x1f); /* request software versions */
sendetx(&buf, parse);
sendcmd(&buf, 0x2c); /* set operating parameters */
sendbyte(&buf, 4); /* static */
sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
sendflt(&buf, 12.0); /* PDOP mask = 12 */
sendflt(&buf, 8.0); /* PDOP switch level = 8 */
sendetx(&buf, parse);
sendcmd(&buf, 0x22); /* fix mode select */
sendbyte(&buf, 0); /* automatic */
sendetx(&buf, parse);
sendcmd(&buf, 0x28); /* request system message */
sendetx(&buf, parse);
sendcmd(&buf, 0x8e); /* superpacket fix */
sendbyte(&buf, 0x2); /* binary mode */
sendetx(&buf, parse);
sendcmd(&buf, 0x35); /* set I/O options */
sendbyte(&buf, 0); /* no position output */
sendbyte(&buf, 0); /* no velocity output */
sendbyte(&buf, 7); /* UTC, compute on seconds, send only on request */
sendbyte(&buf, 0); /* no raw measurements */
sendetx(&buf, parse);
sendcmd(&buf, 0x2f); /* request UTC correction data */
sendetx(&buf, parse);
return 0;
}
else
return 1;
}
#if defined(RAWDCF_SETDTR)
/*--------------------------------------------------
* rawdcf_init - set up modem lines for RAWDCF receivers
*/
#if defined(TIOCMSET) && (defined(TIOCM_DTR) || defined(CIOCM_DTR))
static int
rawdcf_init(parse)
struct parseunit *parse;
{
/*
* You can use the RS232 to supply the power for a DCF77 receiver.
* Here a voltage between the DTR and the RTS line is used. Unfortunately
* the name has changed from CIOCM_DTR to TIOCM_DTR recently.
*/
#ifdef TIOCM_DTR
int sl232 = TIOCM_DTR; /* turn on DTR for power supply */
#else
int sl232 = CIOCM_DTR; /* turn on DTR for power supply */
#endif
if (ioctl(parse->generic->io.fd, TIOCMSET, &sl232) == -1)
{
msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: ioctl(fd, TIOCMSET, [C|T]IOCM_DTR): %m", CLK_UNIT(parse->peer));
}
return 0;
}
#else
static int
rawdcf_init(parse)
struct parseunit *parse;
{
msyslog(LOG_NOTICE, "PARSE receiver #%d: rawdcf_init: WARNING: OS interface incapable of setting DTR to power DCF modules", CLK_UNIT(parse->peer));
return 0;
}
#endif /* DTR initialisation type */
#endif /* RAWDCF SET DTR option */
#else /* defined(REFCLOCK) && defined(PARSE) */
int refclock_parse_bs;
#endif /* defined(REFCLOCK) && defined(PARSE) */
/*
* History:
*
* refclock_parse.c,v
* Revision 3.103 1997/07/12 15:35:16 kardel
* fixed allocation failure for refclock_states string
*
* Revision 3.102 1997/07/06 14:11:55 kardel
* incread internal buffers
* changed filter paramters for PZF535 and GPS166 for more filtering
* removed old useless code fragments
*
* Revision 3.101 1997/04/13 10:05:37 kardel
* 3.5.90 reconcilation
*
* Revision 3.100 1997/04/06 17:37:29 kardel
* Make Hopf clock a fixed format to cope with Meinberg clocks
*
* Revision 3.99 1997/02/08 00:14:11 kardel
* 3.5.89.3 reconcilation
*
* Revision 3.98 1997/01/19 14:11:59 kardel
* removed superfluous functions
*
* Revision 3.97 1997/01/19 12:46:01 kardel
* 3-5.88.1 reconcilation
*
* Revision 3.96 1996/12/01 16:05:49 kardel
* freeze for 5.86.12.2 PARSE-Patch
*
* Revision 3.95 1996/12/01 13:13:47 kardel
* POP "parse" streams module at shutdown
*
* Revision 3.94 1996/11/24 20:10:54 kardel
* RELEASE_5_86_12_2 reconcilation
*
* Revision 3.93 1996/11/16 19:13:47 kardel
* Added DIEM receiver
*
* Revision 3.92 1996/10/05 13:30:29 kardel
* general update
*
* Revision 3.91 1996/06/01 17:05:53 kardel
* cut back revision log
*
* Revision 3.90 1996/06/01 16:45:19 kardel
* cleaned up DTR setup for RAWDCF
*
* Revision 3.88 1995/12/18 00:11:24 kardel
* undo RCC8000 modification
*
* Revision 3.87 1995/12/17 22:54:34 kardel
* fixed HOPF description
*
* Revision 3.86 1995/12/17 22:24:29 kardel
* HOPF 6021 Funkuhr added
*
* Revision 3.85 1995/10/16 01:49:50 duwe
* look for sys/ppsclock.h in the right place
*
* Revision 3.84 1995/10/06 15:58:01 kardel
* auto init for Trimble TAIP
*
* Revision 3.83 1995/09/09 16:52:26 kardel
* more space for refclock variables
*
* Revision 3.82 1995/09/03 17:54:47 kardel
* use unsigned characters for input
*
* Revision 3.81 1995/08/26 07:27:20 kardel
* Trimble TAIP NOPOLL + RECOVERY
*
* Revision 3.80 1995/07/29 08:04:17 kardel
* unsigned char/int problem
*
* Revision information 3.1 - 3.79 from log deleted 1996/06/01 kardel
*
*/