/*
 * refclock_jjy - clock driver for JJY receivers
 *
 * WARNING: Some modes of this driver depend on the system clock for
 * year disambiguation.  They will thus not be usable for recovery if
 * the system clock is trashed.
 */

/**********************************************************************/
/*								      */
/* Copyright Takao Abe.  All rights reserved.			      */
/* SPDX-License-Identifier: BSD-3-Clause			      */
/*								      */
/*  This driver is developed in my private time, and is opened as     */
/*  voluntary contributions for the NTP.			      */
/*  The manufacturer of the JJY receiver has not participated in      */
/*  a development of this driver.				      */
/*  The manufacturer does not warrant anything about this driver,     */
/*  and is not liable for anything about this driver.		      */
/*								      */
/**********************************************************************/
/*								      */
/*  Author     Takao Abe					      */
/*  Email      takao_abe@xurb.jp				      */
/*  Homepage   http://www.bea.hi-ho.ne.jp/abetakao/		      */
/*								      */
/*  The email address abetakao@bea.hi-ho.ne.jp is never read	      */
/*  from 2010, because a few filtering rule are provided by the	      */
/*  "hi-ho.ne.jp", and lots of spam mail are reached.		      */
/*  New email address for supporting the refclock_jjy is	      */
/*  takao_abe@xurb.jp						      */
/*								      */
/**********************************************************************/
/*								      */
/*  History							      */
/*								      */
/*  2001/07/15							      */
/*    [New]    Support the Tristate Ltd. JJY receiver		      */
/*								      */
/*  2001/08/04							      */
/*    [Change] Log to clockstats even if bad reply		      */
/*    [Fix]    PRECISION = (-3) (about 100 ms)			      */
/*    [Add]    Support the C-DEX Co.Ltd. JJY receiver		      */
/*								      */
/*  2001/12/04							      */
/*    [Fix]    C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp )      */
/*								      */
/*  2002/07/12							      */
/*    [Fix]    Portability for FreeBSD ( patched by the user )	      */
/*								      */
/*  2004/10/31							      */
/*    [Change] Command send timing for the Tristate Ltd. JJY receiver */
/*	       JJY-01 ( Firmware version 2.01 )			      */
/*	       Thanks to Andy Taki for testing under FreeBSD	      */
/*								      */
/*  2004/11/28							      */
/*    [Add]    Support the Echo Keisokuki LT-2000 receiver	      */
/*								      */
/*  2006/11/04							      */
/*    [Fix]    C-DEX JST2000					      */
/*	       Thanks to Hideo Kuramatsu for the patch		      */
/*								      */
/*  2009/04/05							      */
/*    [Add]    Support the CITIZEN T.I.C JJY-200 receiver	      */
/*								      */
/*  2010/11/20							      */
/*    [Change] Bug 1618 ( Harmless )				      */
/*	       Code clean up ( Remove unreachable codes ) in	      */
/*	       jjy_start()					      */
/*    [Change] Change clockstats format of the Tristate JJY01/02      */
/*	       Issues more command to get the status of the receiver  */
/*	       when "flag1 1" is specified	      */
/*	       ( DATE,STIM -> DCST,STUS,DATE,STIM )		      */
/*								      */
/*  2011/04/30							      */
/*    [Add]    Support the Tristate Ltd. TS-GPSclock-01		      */
/*								      */
/*  2015/03/29							      */
/*    [Add]    Support the Telephone JJY			      */
/*    [Change] Split the start up routine into each JJY receivers.    */
/*             Change raw data internal bufferring process            */
/*             Change over midnight handling of TS-JJY01 and TS-GPS01 */
/*             to put DATE command between before and after TIME's.   */
/*             Unify the writing clockstats of all JJY receivers.     */
/*								      */
/*  2015/05/15							      */
/*    [Add]    Support the SEIKO TIME SYSTEMS TDC-300		      */
/*								      */
/*  2015/05/08							      */
/*    [Fix]    C-DEX JST2000                                          */
/*             Thanks to Mr. Kuramatsu for the report and the patch.  */
/*								      */
/*  2017/04/30							      */
/*    [Change] Avoid a wrong report of the coverity static analysis   */
/*             tool. ( The code is harmless and has no bug. )	      */
/*             teljjy_conn_send()				      */
/*								      */
/**********************************************************************/

#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>

#include "ntpd.h"
#include "ntp_io.h"
#include "ntp_tty.h"
#include "ntp_refclock.h"
#include "ntp_calendar.h"
#include "ntp_stdlib.h"
#include "ntp_debug.h"

/**********************************************************************/

/*
 * Interface definitions
 */
#define	DEVICE  	"/dev/jjy%d"	/* device name and unit */
#define	SPEED232_TRISTATE_JJY01		B9600   /* UART speed (9600 baud) */
#define	SPEED232_CDEX_JST2000		B9600   /* UART speed (9600 baud) */
#define	SPEED232_ECHOKEISOKUKI_LT2000	B9600   /* UART speed (9600 baud) */
#define	SPEED232_CITIZENTIC_JJY200	B4800   /* UART speed (4800 baud) */
#define	SPEED232_TRISTATE_GPSCLOCK01	B38400  /* USB  speed (38400 baud) */
#define	SPEED232_SEIKO_TIMESYS_TDC_300	B2400   /* UART speed (2400 baud) */
#define	SPEED232_TELEPHONE		B2400   /* UART speed (4800 baud) */
#define	REFID   	"JJY"		/* reference ID */
#define	NAME		"JJY"		/* shortname */
#define	DESCRIPTION	"JJY Receiver"
#define	PRECISION	(-3)		/* precision assumed (about 100 ms) */

/*
 * JJY unit control structure
 */

struct jjyRawDataBreak {
	const char	*pString ;
	int 	iLength ;
} ;

#define	MAX_TIMESTAMP	6
#define	MAX_RAWBUF   	100
#define	MAX_LOOPBACK	5

struct jjyunit {
/* Set up by the function "jjy_start_xxxxxxxx" */
	char	unittype ;	    /* UNITTYPE_XXXXXXXXXX */
	short   operationmode ;	    /* Echo Keisokuki LT-2000 */
	int 	linespeed ;         /* SPEED232_XXXXXXXXXX */
	short	linediscipline ;    /* LDISC_STD or LDISC_RAW */
/* Receiving data */
	bool	bInitError ;        /* Set by jjy_start if any error during initialization */
	short	iProcessState ;     /* JJY_PROCESS_STATE_XXXXXX */
	bool	bReceiveFlag ;      /* Set and reset by jjy_receive */
	bool	bLineError ;	    /* Reset by jjy_poll / Set by jjy_receive_xxxxxxxx*/
	short	iCommandSeq ;       /* 0:Idle  Non-Zero:Issued */
	short	iReceiveSeq ;
	int 	iLineCount ;
	int 	year, month, day, hour, minute, second, msecond ;
	int 	leapsecond ;
	int 	iTimestampCount ;   /* TS-JJY01, TS-GPS01, Telephone-JJY */
	int 	iTimestamp [ MAX_TIMESTAMP ] ;  /* Serial second ( 0 - 86399 ) */
/* LDISC_RAW only */
	char	sRawBuf [ MAX_RAWBUF ] ;
	int 	iRawBufLen ;
	struct	jjyRawDataBreak *pRawBreak ;
	bool	bWaitBreakString ;
	char	sLineBuf [ MAX_RAWBUF ] ;
	int 	iLineBufLen ;
	char	sTextBuf [ MAX_RAWBUF ] ;
	int 	iTextBufLen ;
	bool	bSkipCntrlCharOnly ;
/* Telephone JJY auto measurement of the loopback delay */
	bool	bLoopbackMode ;
	short	iLoopbackCount ;
	struct	timespec sendTime[MAX_LOOPBACK], delayTime[MAX_LOOPBACK] ;
	bool	bLoopbackTimeout[MAX_LOOPBACK] ;
	short	iLoopbackValidCount ;
/* Telephone JJY timer */
	short	iTeljjySilentTimer ;
	short	iTeljjyStateTimer ;
/* Telephone JJY control finite state machine */
	short	iClockState ;
	short	iClockEvent ;
	short	iClockCommandSeq ;
/* Modem timer */
	short	iModemSilentCount ;
	short	iModemSilentTimer ;
	short	iModemStateTimer ;
/* Modem control finite state machine */
	short	iModemState ;
	short	iModemEvent ;
	short	iModemCommandSeq ;
};

#define	UNITTYPE_TRISTATE_JJY01		1
#define	UNITTYPE_CDEX_JST2000		2
#define	UNITTYPE_ECHOKEISOKUKI_LT2000  	3
#define	UNITTYPE_CITIZENTIC_JJY200  	4
#define	UNITTYPE_TRISTATE_GPSCLOCK01	5
#define	UNITTYPE_SEIKO_TIMESYS_TDC_300	6
#define	UNITTYPE_TELEPHONE		100

#define	JJY_PROCESS_STATE_IDLE   	0
#define	JJY_PROCESS_STATE_POLL   	1
#define	JJY_PROCESS_STATE_RECEIVE	2
#define	JJY_PROCESS_STATE_DONE   	3
#define	JJY_PROCESS_STATE_ERROR  	4

/**********************************************************************/

/*
 *  Function calling structure
 *
 *  jjy_start
 *   |--  jjy_start_tristate_jjy01
 *   |--  jjy_start_cdex_jst2000
 *   |--  jjy_start_echokeisokuki_lt2000
 *   |--  jjy_start_citizentic_jjy200
 *   |--  jjy_start_tristate_gpsclock01
 *   |--  jjy_start_seiko_tsys_tdc_300
 *   |--  jjy_start_telephone
 *
 *  jjy_poll
 *   |--  jjy_poll_tristate_jjy01
 *   |--  jjy_poll_cdex_jst2000
 *   |--  jjy_poll_echokeisokuki_lt2000
 *   |--  jjy_poll_citizentic_jjy200
 *   |--  jjy_poll_tristate_gpsclock01
 *   |--  jjy_poll_seiko_tsys_tdc_300
 *   |--  jjy_poll_telephone
 *         |--  teljjy_control
 *               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *                     |--  modem_connect
 *                           |--  modem_control
 *                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *
 *  jjy_receive
 *   |
 *   |--  jjy_receive_tristate_jjy01
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_cdex_jst2000
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_echokeisokuki_lt2000
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_citizentic_jjy200
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_tristate_gpsclock01
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_seiko_tsys_tdc_300
 *   |     |--  jjy_synctime
 *   |--  jjy_receive_telephone
 *         |--  modem_receive
 *         |     |--  modem_control
 *         |           |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *         |--  teljjy_control
 *               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *                     |--  jjy_synctime
 *                     |--  modem_disconnect
 *                           |--  modem_control
 *                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *
 *  jjy_timer
 *   |--  jjy_timer_telephone
 *         |--  modem_timer
 *         |     |--  modem_control
 *         |           |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *         |--  teljjy_control
 *               |--  teljjy_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *                     |--  modem_disconnect
 *                           |--  modem_control
 *                                 |--  modem_XXXX_YYYY ( XXXX_YYYY is an event handler name. )
 *
 * Function prototypes
 */

static	bool 	jjy_start			(int, struct peer *);
static	int 	jjy_start_tristate_jjy01	(int, struct peer *, struct jjyunit *);
static	int 	jjy_start_cdex_jst2000		(int, struct peer *, struct jjyunit *);
static	int 	jjy_start_echokeisokuki_lt2000	(int, struct peer *, struct jjyunit *);
static	int 	jjy_start_citizentic_jjy200	(int, struct peer *, struct jjyunit *);
static	int 	jjy_start_tristate_gpsclock01	(int, struct peer *, struct jjyunit *);
static	int 	jjy_start_seiko_tsys_tdc_300	(int, struct peer *, struct jjyunit *);
static	bool 	jjy_start_telephone		(int, struct peer *, struct jjyunit *);

static	void	jjy_poll		    	(int, struct peer *);
static	void	jjy_poll_tristate_jjy01	    	(int, struct peer *);
static	void	jjy_poll_cdex_jst2000	    	(int, struct peer *);
static	void	jjy_poll_echokeisokuki_lt2000	(int, struct peer *);
static	void	jjy_poll_citizentic_jjy200	(int, struct peer *);
static	void	jjy_poll_tristate_gpsclock01	(int, struct peer *);
static	void	jjy_poll_seiko_tsys_tdc_300	(int, struct peer *);
static	void	jjy_poll_telephone		(int, struct peer *);

static	void	jjy_receive			(struct recvbuf *);
static	int 	jjy_receive_tristate_jjy01	(struct recvbuf *);
static	int 	jjy_receive_cdex_jst2000	(struct recvbuf *);
static	int 	jjy_receive_echokeisokuki_lt2000 (struct recvbuf *);
static  int 	jjy_receive_citizentic_jjy200	(struct recvbuf *);
static	int 	jjy_receive_tristate_gpsclock01	(struct recvbuf *);
static	int 	jjy_receive_seiko_tsys_tdc_300	(struct recvbuf *);
static	int 	jjy_receive_telephone		(struct recvbuf *);

static	void	jjy_timer			(int, struct peer *);
static	void	jjy_timer_telephone		(int, struct peer *);

static	void	jjy_synctime			( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	void	jjy_write_clockstats		( struct peer *, int, const char* ) ;

static	int 	getRawDataBreakPosition		( struct jjyunit *, int ) ;

static	short	getModemState			( struct jjyunit * ) ;
static	int 	isModemStateConnect		( short ) ;
static	int 	isModemStateDisconnect		( short ) ;
static	int 	isModemStateTimerOn		( struct jjyunit * ) ;
static	void	modem_connect			( int, struct peer * ) ;
static	void	modem_disconnect		( int, struct peer * ) ;
static	int 	modem_receive			( struct recvbuf * ) ;
static	void	modem_timer			( int, struct peer * );

static	void	printableString ( char*, int, const char*, int ) ;

/*
 * Transfer vector
 */
struct	refclock refclock_jjy = {
	NAME,		/* basename of driver */
	jjy_start,	/* start up driver */
	NULL,		/* shutdown driver in standard way */
	jjy_poll,	/* transmit poll message */
	NULL,		/* control - not used */
	NULL,		/* init - not used */
	jjy_timer	/* 1 second interval timer */
};

/*
 * Local constants definition
 */

/* gcc 7 says 100 is too short */
#define	MAX_LOGTEXT	192

/* Local constants definition for the return code of the jjy_receive_xxxxxxxx */

#define	JJY_RECEIVE_DONE	0
#define	JJY_RECEIVE_SKIP	1
#define	JJY_RECEIVE_UNPROCESS	2
#define	JJY_RECEIVE_WAIT	3
#define	JJY_RECEIVE_ERROR	4

/* Local constants definition for the 2nd parameter of the jjy_write_clockstats */

/* #define	JJY_CLOCKSTATS_MARK_NONE 0   UNUSED */
#define	JJY_CLOCKSTATS_MARK_JJY 	1
#define	JJY_CLOCKSTATS_MARK_SEND	2
#define	JJY_CLOCKSTATS_MARK_RECEIVE	3
#define	JJY_CLOCKSTATS_MARK_INFORMATION	4
#define	JJY_CLOCKSTATS_MARK_ATTENTION	5
#define	JJY_CLOCKSTATS_MARK_WARNING	6
#define	JJY_CLOCKSTATS_MARK_ERROR	7
#define	JJY_CLOCKSTATS_MARK_BUG 	8

/* Local constants definition for the clockstats messages */

#define	JJY_CLOCKSTATS_MESSAGE_ECHOBACK         	"* Echoback"
#define	JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY     	"* Ignore replay : [%s]"
#define	JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2  	"* Over midnight : timestamp=%d, %d"
/* UNUSED
 * #define	JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_3  	"* Over midnight : timestamp=%d, %d, %d"
 */
#define	JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE 	"* Unsure timestamp : %s"
#define	JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY   	"* Loopback delay : %d.%03d mSec."
#define	JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST     	"* Delay adjustment : %d mSec. ( valid=%hd/%d )"
#define	JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST   	"* Delay adjustment : None ( valid=%hd/%d )"

#define	JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY     	"# Unexpected reply : [%s]"
#define	JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH     	"# Invalid length : length=%d"
#define	JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY     	"# Too many reply : count=%d"
#define	JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY      	"# Invalid reply : [%s]"
#define	JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2       	"# Slow reply : timestamp=%d, %d"
/* UNUSED
 * #define	JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_3       	"# Slow reply : timestamp=%d, %d, %d"
 */
#define	JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE	"# Invalid date : rc=%d year=%d month=%d day=%d"
#define	JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME	"# Invalid time : rc=%d hour=%d minute=%d second=%d"
#define	JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME	"# Invalid time : rc=%d year=%d month=%d day=%d hour=%d minute=%d second=%d"
#define	JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP	"# Invalid leap : leapsecond=[%s]"
/* UNUSED
 * #define	JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_STATUS	"# Invalid status : status=[%s]"
 */

/* Debug print macro */

#ifdef	DEBUG
#define	DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen)	{ if ( debug ) { printf ( "refclock_jjy.c : %s : iProcessState=%d bLineError=%d iCommandSeq=%d iLineCount=%d iTimestampCount=%d iLen=%d\n", sFunc, up->iProcessState, up->bLineError, up->iCommandSeq, up->iLineCount, up->iTimestampCount, iLen ) ; } }
#else
#define	DEBUG_PRINTF_JJY_RECEIVE(sFunc,iLen)
#endif

/**************************************************************************************************/
/*  jjy_start - open the devices and initialize data for processing                               */
/**************************************************************************************************/
static bool
jjy_start ( int unit, struct peer *peer )
{

	struct	refclockproc *pp ;
	struct	jjyunit      *up ;
	int 	rc ;
	int 	fd ;
	char	sDeviceName [ sizeof(DEVICE) + 10 ], sLog [ MAX_LOGTEXT ] ;

        DPRINT(1, ("refclock_jjy.c: jjy_start: %s  mode=%u  dev=%s  unit=%d\n",
			 socktoa(&peer->srcadr), peer->cfg.mode, DEVICE, unit )) ;

	/* Allocate memory for the unit structure */
	up = emalloc_zero( sizeof(*up) ) ;
	if ( up == NULL ) {
		msyslog ( LOG_ERR, "refclock_jjy.c : jjy_start : emalloc" ) ;
		return false ;
	}

	up->bInitError = false ;
	up->iProcessState = JJY_PROCESS_STATE_IDLE ;
	up->bReceiveFlag = false ;
	up->iCommandSeq = 0 ;
	up->iLineCount = 0 ;
	up->iTimestampCount = 0 ;
	up->bWaitBreakString = false ;
	up->iRawBufLen = up->iLineBufLen = up->iTextBufLen = 0 ;
	up->bSkipCntrlCharOnly = true ;

	/* Set up the device name */
	snprintf( sDeviceName, sizeof(sDeviceName), DEVICE, unit ) ;

	snprintf(sLog, sizeof(sLog), "subtype=%u dev=%s",
                 peer->cfg.mode, sDeviceName ) ;
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

	/*
	 * peer->cfg.mode is a subtype number specified by "jjy subtype N" in the ntp.conf
	 */
	switch ( peer->cfg.mode ) {
	case 0 :
	case 1 :
		rc = jjy_start_tristate_jjy01 ( unit, peer, up ) ;
		break ;
	case 2 :
		rc = jjy_start_cdex_jst2000 ( unit, peer, up ) ;
		break ;
	case 3 :
		rc = jjy_start_echokeisokuki_lt2000 ( unit, peer, up ) ;
		break ;
	case 4 :
		rc = jjy_start_citizentic_jjy200 ( unit, peer, up ) ;
		break ;
	case 5 :
		rc = jjy_start_tristate_gpsclock01 ( unit, peer, up ) ;
		break ;
	case 6 :
		rc = jjy_start_seiko_tsys_tdc_300 ( unit, peer, up ) ;
		break ;
	case 100 :
		rc = jjy_start_telephone ( unit, peer, up ) ;
		break ;
	default :
		if ( 101 <= peer->cfg.mode && peer->cfg.mode <= 180 ) {
			rc = jjy_start_telephone ( unit, peer, up ) ;
		} else {
		    msyslog (LOG_ERR,
			"JJY receiver [ %s subtype %u ] : Unsupported mode",
			socktoa(&peer->srcadr), peer->cfg.mode ) ;
		    free ( (void*) up ) ;
		    return false ;
		}
	}

	if ( rc != 0 ) {
		msyslog(LOG_ERR,
                        "REFCLOCK: JJY receiver [ %s subtype %u ] : Initialize error",
			socktoa(&peer->srcadr), peer->cfg.mode ) ;
		free ( (void*) up ) ;
		return false ;
	}

	/* Open the device */
	fd = refclock_open ( sDeviceName, (unsigned int)up->linespeed,
                            (unsigned int)up->linediscipline ) ;
	if ( fd <= 0 ) {
		free ( (void*) up ) ;
		/* coverity[leaked_handle] */
		return false ;
	}

	/*
	 * Initialize variables
	 */
	pp = peer->procptr ;

	pp->clockname     = NAME;
	pp->clockdesc	  = DESCRIPTION ;
	pp->unitptr       = up ;
	pp->io.clock_recv = jjy_receive ;
	pp->io.srcclock   = peer ;
	pp->io.datalen	  = 0 ;
	pp->io.fd	  = fd ;
	if ( ! io_addclock(&pp->io) ) {
		close ( fd ) ;
		pp->io.fd = -1 ;
		free ( up ) ;
		pp->unitptr = NULL ;
		return false ;
	}
	memcpy( (char*)&pp->refid, REFID, strlen(REFID) ) ;

	peer->precision = PRECISION ;

	snprintf( sLog, sizeof(sLog), "minpoll=%d maxpoll=%d", peer->cfg.minpoll, peer->cfg.maxpoll ) ;
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

	return true;

}

/**************************************************************************************************/
/*  jjy_receive - receive data from the serial interface                                          */
/**************************************************************************************************/
static void
jjy_receive ( struct recvbuf *rbufp )
{
#ifdef DEBUG
	static const char *sFunctionName = "jjy_receive" ;
#endif

	struct jjyunit	    *up ;
	struct refclockproc *pp ;
	struct peer	    *peer;

	l_fp	tRecvTimestamp;		/* arrival timestamp */
	int 	rc ;
	char	*pBuf, sLogText [ MAX_LOGTEXT ] ;
	int 	i, j, iReadRawBuf, iBreakPosition ;

	/*
	 * Initialize pointers and read the timecode and timestamp
	 */
	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	/*
	 * Get next input line
	 */
	if ( up->linediscipline == LDISC_RAW ) {

		pp->lencode  = (int)refclock_gtraw ( rbufp, pp->a_lastcode, BMAX-1, &tRecvTimestamp ) ;
		/* 3rd argument can be BMAX, but the coverity scan tool claim "Memory - corruptions  (OVERRUN)" */
		/* "a_lastcode" is defined as "char a_lastcode[BMAX]" in the ntp_refclock.h */
		/* To avoid its claim, pass the value BMAX-1. */

		/*
		 * Append received characters to temporary buffer
		 */
		for ( i = 0 ;
		      i < pp->lencode && up->iRawBufLen < MAX_RAWBUF - 2 ;
		      i ++ , up->iRawBufLen ++ ) {
			up->sRawBuf[up->iRawBufLen] = pp->a_lastcode[i] ;
		}
		up->sRawBuf[up->iRawBufLen] = 0 ;


	} else {

		pp->lencode  = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;

	}
#ifdef DEBUG
	printf( "\nrefclock_jjy.c : %s : Len=%d  ", sFunctionName, pp->lencode ) ;
	for ( i = 0 ; i < pp->lencode ; i ++ ) {
		if ( iscntrl( pp->a_lastcode[i] & 0x7F ) ) {
			printf("<x%02X>", (unsigned)(pp->a_lastcode[i] & 0xFF));
		} else {
			printf( "%c", pp->a_lastcode[i] ) ;
		}
	}
	printf( "\n" ) ;
#endif

	/*
	 * The reply with <CR><LF> gives a blank line
	 */

	if ( pp->lencode == 0 ) return ;

	/*
	 * Receiving data is not expected
	 */

	if ( up->iProcessState == JJY_PROCESS_STATE_IDLE
	  || up->iProcessState == JJY_PROCESS_STATE_DONE
	  || up->iProcessState == JJY_PROCESS_STATE_ERROR ) {
		/* Discard received data */
		up->iRawBufLen = 0 ;
	        DPRINT(1, ( "refclock_jjy.c : %s : Discard received data\n", sFunctionName )) ;
		return ;
	}

	/*
	 * We get down to business
	 */

	pp->lastrec = tRecvTimestamp ;

	up->iLineCount ++ ;

	up->iProcessState = JJY_PROCESS_STATE_RECEIVE ;
	up->bReceiveFlag = true ;

	iReadRawBuf = 0 ;
	iBreakPosition = up->iRawBufLen - 1 ;
	for ( ; up->iProcessState == JJY_PROCESS_STATE_RECEIVE ; ) {

		if ( up->linediscipline == LDISC_RAW ) {

			if ( up->bWaitBreakString ) {
				iBreakPosition = getRawDataBreakPosition( up, iReadRawBuf ) ;
				if ( iBreakPosition == -1 ) {
					/* Break string have not come yet */
					if ( up->iRawBufLen < MAX_RAWBUF - 2
					  || iReadRawBuf > 0 ) {
						/* Temporary buffer is not full */
						break ;
					} else {
						/* Temporary buffer is full */
						iBreakPosition = up->iRawBufLen - 1 ;
					}
				}
			} else {
				iBreakPosition = up->iRawBufLen - 1 ;
			}

			/* Copy characters from temporary buffer to process buffer */
			up->iLineBufLen = up->iTextBufLen = 0 ;
			for ( i = iReadRawBuf ; i <= iBreakPosition ; i ++ ) {

				/* Copy all characters */
				up->sLineBuf[up->iLineBufLen] = up->sRawBuf[i] ;
				up->iLineBufLen ++ ;

				/* Copy printable characters */
				if ( ! iscntrl( (int)up->sRawBuf[i] ) ) {
					up->sTextBuf[up->iTextBufLen] = up->sRawBuf[i] ;
					up->iTextBufLen ++ ;
				}

			}
			up->sLineBuf[up->iLineBufLen] = 0 ;
			up->sTextBuf[up->iTextBufLen] = 0 ;
#ifdef DEBUG
			printf( "refclock_jjy.c : %s : up->iLineBufLen=%d up->iTextBufLen=%d\n",
				 sFunctionName, up->iLineBufLen, up->iTextBufLen ) ;
#endif

			if ( up->bSkipCntrlCharOnly && up->iTextBufLen == 0 ) {
#ifdef DEBUG
				printf( "refclock_jjy.c : %s : Skip cntrl char only : up->iRawBufLen=%d iReadRawBuf=%d iBreakPosition=%d\n",
					 sFunctionName, up->iRawBufLen, iReadRawBuf, iBreakPosition ) ;
#endif
				if ( iBreakPosition + 1 < up->iRawBufLen ) {
					iReadRawBuf = iBreakPosition + 1 ;
					continue ;
				} else {
					break ;
				}

			}

		}

		if ( up->linediscipline == LDISC_RAW ) {
			pBuf = up->sLineBuf ;
		} else {
			pBuf = pp->a_lastcode ;
		}

		strlcpy( sLogText, pBuf, sizeof(sLogText) ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_RECEIVE, sLogText ) ;

		switch ( up->unittype ) {

		case UNITTYPE_TRISTATE_JJY01 :
			rc = jjy_receive_tristate_jjy01  ( rbufp ) ;
			break ;

		case UNITTYPE_CDEX_JST2000 :
			rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
			break ;

		case UNITTYPE_ECHOKEISOKUKI_LT2000 :
			rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ;
			break ;

		case UNITTYPE_CITIZENTIC_JJY200 :
			rc = jjy_receive_citizentic_jjy200 ( rbufp ) ;
			break ;

		case UNITTYPE_TRISTATE_GPSCLOCK01 :
			rc = jjy_receive_tristate_gpsclock01 ( rbufp ) ;
			break ;

		case UNITTYPE_SEIKO_TIMESYS_TDC_300 :
			rc = jjy_receive_seiko_tsys_tdc_300 ( rbufp ) ;
			break ;

		case UNITTYPE_TELEPHONE :
			rc = jjy_receive_telephone ( rbufp ) ;
			break ;

		default :
			rc = JJY_RECEIVE_ERROR ;
			break ;

		}

		switch ( rc ) {
		case JJY_RECEIVE_DONE :
		case JJY_RECEIVE_SKIP :
			up->iProcessState = JJY_PROCESS_STATE_DONE ;
			break ;
		case JJY_RECEIVE_ERROR :
			up->iProcessState = JJY_PROCESS_STATE_ERROR ;
			break ;
		default :
			break ;
		}

		if ( up->linediscipline == LDISC_RAW ) {
			if ( rc == JJY_RECEIVE_UNPROCESS ) {
				break ;
			}
			iReadRawBuf = iBreakPosition + 1 ;
			if ( iReadRawBuf >= up->iRawBufLen ) {
				/* Processed all received data */
				break ;
			}
		}

		if ( up->linediscipline == LDISC_STD ) {
			break ;
		}

	}

	if ( up->linediscipline == LDISC_RAW && iReadRawBuf > 0 ) {
		for ( i = 0, j = iReadRawBuf ; j < up->iRawBufLen ; i ++, j++ ) {
			up->sRawBuf[i] = up->sRawBuf[j] ;
		}
		up->iRawBufLen -= iReadRawBuf ;
		if ( up->iRawBufLen < 0 ) {
			up->iRawBufLen = 0 ;
		}
	}

	up->bReceiveFlag = false ;

}

/**************************************************************************************************/

static int
getRawDataBreakPosition ( struct jjyunit *up, int iStart )
{

	int 	i, j ;

	if ( iStart >= up->iRawBufLen ) {
#ifdef DEBUG
		printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ;
#endif
		return -1 ;
	}

	for ( i = iStart ; i < up->iRawBufLen ; i ++ ) {

		for ( j = 0 ; up->pRawBreak[j].pString != NULL ; j ++ ) {

			if ( i + up->pRawBreak[j].iLength <= up->iRawBufLen ) {

				if ( strncmp( up->sRawBuf + i,
				    up->pRawBreak[j].pString,
				    (size_t)up->pRawBreak[j].iLength ) == 0 ) {

#ifdef DEBUG
					printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=%d\n",
						iStart, i + up->pRawBreak[j].iLength - 1 ) ;
#endif
					return i + up->pRawBreak[j].iLength - 1 ;

				}
			}
		}
	}

#ifdef DEBUG
	printf( "refclock_jjy.c : getRawDataBreakPosition : iStart=%d return=-1\n", iStart ) ;
#endif
	return -1 ;

}

/**************************************************************************************************/
/*  jjy_poll - called by the transmit procedure                                                   */
/**************************************************************************************************/
static void
jjy_poll ( int unit, struct peer *peer )
{

	char	sLog [ MAX_LOGTEXT ], sReach [ 9 ] ;

	struct jjyunit      *up;
	struct refclockproc *pp;

	pp = peer->procptr;
	up = pp->unitptr ;

	if ( up->bInitError ) {
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, "Ignore polling because of error during initializing" ) ;
		return ;
	}

	if ( pp->polls > 0  &&  up->iLineCount == 0 ) {
		/*
		 * No reply for last command
		 */
		refclock_report ( peer, CEVNT_TIMEOUT ) ;
	}

	pp->polls ++ ;

	sReach[0] = peer->reach & 0x80 ? '1' : '0' ;
	sReach[1] = peer->reach & 0x40 ? '1' : '0' ;
	sReach[2] = peer->reach & 0x20 ? '1' : '0' ;
	sReach[3] = peer->reach & 0x10 ? '1' : '0' ;
	sReach[4] = peer->reach & 0x08 ? '1' : '0' ;
	sReach[5] = peer->reach & 0x04 ? '1' : '0' ;
	sReach[6] = peer->reach & 0x02 ? '1' : '0' ;
	sReach[7] = 0 ; /* This poll */
	sReach[8] = 0 ;

	snprintf(sLog, sizeof(sLog), "polls=%lu reach=%s", pp->polls, sReach);
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ;

	up->iProcessState = JJY_PROCESS_STATE_POLL ;
	up->iCommandSeq = 0 ;
	up->iReceiveSeq = 0 ;
	up->iLineCount = 0 ;
	up->bLineError = false ;
	up->iRawBufLen = 0 ;

	switch ( up->unittype ) {

	case UNITTYPE_TRISTATE_JJY01 :
		jjy_poll_tristate_jjy01  ( unit, peer ) ;
		break ;

	case UNITTYPE_CDEX_JST2000 :
		jjy_poll_cdex_jst2000 ( unit, peer ) ;
		break ;

	case UNITTYPE_ECHOKEISOKUKI_LT2000 :
		jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ;
		break ;

	case UNITTYPE_CITIZENTIC_JJY200 :
		jjy_poll_citizentic_jjy200 ( unit, peer ) ;
		break ;

	case UNITTYPE_TRISTATE_GPSCLOCK01 :
		jjy_poll_tristate_gpsclock01 ( unit, peer ) ;
		break ;

	case UNITTYPE_SEIKO_TIMESYS_TDC_300 :
		jjy_poll_seiko_tsys_tdc_300 ( unit, peer ) ;
		break ;

	case UNITTYPE_TELEPHONE :
		jjy_poll_telephone ( unit, peer ) ;
		break ;

	default :
		break ;

	}

}

/**************************************************************************************************/
/*  jjy_timer - called at one-second intervals                                                    */
/**************************************************************************************************/
static void
jjy_timer ( int unit, struct peer *peer )
{

	struct	refclockproc *pp ;
	struct	jjyunit      *up ;

	DPRINT(1, ( "refclock_jjy.c : jjy_timer\n" )) ;

	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->bReceiveFlag ) {
	        DPRINT(1, ( "refclock_jjy.c : jjy_timer : up->bReceiveFlag= true : Timer skipped.\n" )) ;
		return ;
	}

	switch ( up->unittype ) {

	case UNITTYPE_TELEPHONE :
		jjy_timer_telephone ( unit, peer ) ;
		break ;

	default :
		break ;

	}

}

/**************************************************************************************************/
/*  jjy_synctime                                                                                  */
/**************************************************************************************************/
static void
jjy_synctime ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	char	sLog [ MAX_LOGTEXT ], cStatus ;
	const char	*pStatus ;

	pp->year   = up->year ;
	pp->day    = ymd2yd( up->year, up->month, up->day ) ;
	pp->hour   = up->hour ;
	pp->minute = up->minute ;
	pp->second = up->second ;
	pp->nsec   = up->msecond * 1000000 ;

	/*
	 * JST to UTC
	 */
	pp->hour -= 9 ;
	if ( pp->hour < 0 ) {
		pp->hour += 24 ;
		pp->day -- ;
		if ( pp->day < 1 ) {
			pp->year -- ;
			pp->day  = ymd2yd( pp->year, 12, 31 ) ;
		}
	}

	/*
	 * Process the new sample in the median filter and determine the
	 * timecode timestamp.
	 */

	if ( ! refclock_process( pp ) ) {
		refclock_report( peer, CEVNT_BADTIME ) ;
		return ;
	}

	pp->lastref = pp->lastrec ;

	refclock_receive( peer ) ;

	/*
	 * Write into the clockstats file
	 */
	snprintf ( sLog, sizeof(sLog),
		   "%04d/%02d/%02d %02d:%02d:%02d.%03d JST   ( %04d/%03d %02d:%02d:%02d.%03d UTC )",
		   up->year, up->month, up->day,
		   up->hour, up->minute, up->second, up->msecond,
		   pp->year, pp->day, pp->hour, pp->minute, pp->second,
		   (int)(pp->nsec/1000000) ) ;
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ATTENTION, sLog ) ;

	cStatus = ' ' ;
	pStatus = "" ;

	switch ( peer->status ) {
	case 0 : cStatus = ' ' ; pStatus = "Reject"    ; break ;
	case 1 : cStatus = 'x' ; pStatus = "FalseTick" ; break ;
	case 2 : cStatus = '.' ; pStatus = "Excess"    ; break ;
	case 3 : cStatus = '-' ; pStatus = "Outlier"   ; break ;
	case 4 : cStatus = '+' ; pStatus = "Candidate" ; break ;
	case 5 : cStatus = '#' ; pStatus = "Selected"  ; break ;
	case 6 : cStatus = '*' ; pStatus = "Sys.Peer"  ; break ;
	case 7 : cStatus = 'o' ; pStatus = "PPS.Peer"  ; break ;
	default : break ;
	}

	snprintf ( sLog, sizeof(sLog),
		   "status %d [%c] %s : offset %3.3f mSec. : jitter %3.3f mSec.",
		    peer->status, cStatus, pStatus, peer->offset * 1000, peer->jitter * 1000 ) ;
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The Tristate Ltd. JJY receiver TS-JJY01, TS-JJY02					##*/
/*##												##*/
/*##    refclock jjy unit X subtype 1								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  dcst<CR><LF>          VALID<CR><LF> or INVALID<CR><LF>                                        */
/*  stus<CR><LF>          ADJUSTED<CR><LF> or UNADJUSTED<CR><LF>                                  */
/*  date<CR><LF>          YYYY/MM/DD XXX<CR><LF>                    XXX is the day of the week    */
/*  time<CR><LF>          HH:MM:SS<CR><LF>                          Not used by this driver       */
/*  stim<CR><LF>          HH:MM:SS<CR><LF>                          Reply at just second          */
/*                                                                                                */
/*################################################################################################*/

#define	TS_JJY01_COMMAND_NUMBER_DATE	1
#define	TS_JJY01_COMMAND_NUMBER_TIME	2
#define	TS_JJY01_COMMAND_NUMBER_STIM	3
#define	TS_JJY01_COMMAND_NUMBER_STUS	4
#define	TS_JJY01_COMMAND_NUMBER_DCST	5

/* #define	TS_JJY01_REPLY_DATE     	"yyyy/mm/dd www" UNUSED */
/* #define	TS_JJY01_REPLY_STIM     	"hh:mm:ss" UNUSED */
#define	TS_JJY01_REPLY_STUS_ADJUSTED	"adjusted"
#define	TS_JJY01_REPLY_STUS_UNADJUSTED	"unadjusted"
#define	TS_JJY01_REPLY_DCST_VALID	"valid"
#define	TS_JJY01_REPLY_DCST_INVALID	"invalid"

#define	TS_JJY01_REPLY_LENGTH_DATE           	14	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_TIME           	8	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STIM           	8	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED  	8	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED	10	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_DCST_VALID     	5	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_DCST_INVALID   	7	/* Length without <CR><LF> */

static  struct
{
	const char	commandNumber ;
	const char	*command ;
	int	commandLength ;
	int	iExpectedReplyLength [ 2 ] ;
} tristate_jjy01_command_sequence[] =
{
	{ 0, NULL, 0, { 0, 0 } }, /* Idle */
	{ TS_JJY01_COMMAND_NUMBER_DCST, "dcst\r\n", 6, { TS_JJY01_REPLY_LENGTH_DCST_VALID   , TS_JJY01_REPLY_LENGTH_DCST_INVALID } },
	{ TS_JJY01_COMMAND_NUMBER_STUS, "stus\r\n", 6, { TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED, TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED } },
	{ TS_JJY01_COMMAND_NUMBER_TIME, "time\r\n", 6, { TS_JJY01_REPLY_LENGTH_TIME         , TS_JJY01_REPLY_LENGTH_TIME } },
	{ TS_JJY01_COMMAND_NUMBER_DATE, "date\r\n", 6, { TS_JJY01_REPLY_LENGTH_DATE         , TS_JJY01_REPLY_LENGTH_DATE } },
	{ TS_JJY01_COMMAND_NUMBER_STIM, "stim\r\n", 6, { TS_JJY01_REPLY_LENGTH_STIM         , TS_JJY01_REPLY_LENGTH_STIM } },
	/* End of command */
	{ 0, NULL, 0, { 0, 0 } }
} ;

/**************************************************************************************************/

static int
jjy_start_tristate_jjy01 ( int unit, struct peer *peer, struct jjyunit *up )
{
	UNUSED_ARG(unit);

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-JJY01, TS-JJY02" ) ;

	up->unittype  = UNITTYPE_TRISTATE_JJY01 ;
	up->linespeed = SPEED232_TRISTATE_JJY01 ;
	up->linediscipline = LDISC_STD ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
{
	struct jjyunit	    *up ;
	struct refclockproc *pp ;
	struct peer	    *peer;

	char	*pBuf, sLog [ MAX_LOGTEXT ] ;
	int 	iLen ;
	int 	rc ;

	const char *pCmd ;
	int 	iCmdLen ;

	/* Initialize pointers  */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_jjy01", iLen ) ;

	/* Check expected reply */

	if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) {
		/* Command sequence has not been started, or has been completed */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* Check reply length */

	if ( iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[0]
	  && iLen != tristate_jjy01_command_sequence[up->iCommandSeq].iExpectedReplyLength[1] ) {
		/* Unexpected reply length */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* Parse reply */

	switch ( tristate_jjy01_command_sequence[up->iCommandSeq].commandNumber ) {

	case TS_JJY01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD WWW */

		rc = sscanf ( pBuf, "%4d/%2d/%2d",
			      &up->year, &up->month, &up->day ) ;

		if ( rc != 3 || up->year < 2000 || 2099 <= up->year
		  || up->month < 1 || 12 < up->month
		  || up->day < 1 || 31 < up->day ) {
			/* Invalid date */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
				  rc, up->year, up->month, up->day ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	case TS_JJY01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
	case TS_JJY01_COMMAND_NUMBER_STIM : /* HH:MM:SS */

		if ( up->iTimestampCount >= 2 ) {
			/* Too many time reply */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY,
				  up->iTimestampCount ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		rc = sscanf ( pBuf, "%2d:%2d:%2d",
			      &up->hour, &up->minute, &up->second ) ;

		if ( rc != 3 || up->hour > 23 || up->minute > 59 ||
		     up->second > 60 ) {
			/* Invalid time */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
				  rc, up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

		up->iTimestampCount++ ;

		up->msecond = 0 ;

		break ;

	case TS_JJY01_COMMAND_NUMBER_STUS :

		if ( strncmp( pBuf, TS_JJY01_REPLY_STUS_ADJUSTED,
			     TS_JJY01_REPLY_LENGTH_STUS_ADJUSTED ) == 0
		  || strncmp( pBuf, TS_JJY01_REPLY_STUS_UNADJUSTED,
			     TS_JJY01_REPLY_LENGTH_STUS_UNADJUSTED ) == 0 ) {
			/* Good */
		} else {
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
				  pBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	case TS_JJY01_COMMAND_NUMBER_DCST :

		if ( strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID,
			     TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0
		  || strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID,
			     TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) {
			/* Good */
		} else {
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
				  pBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	default : /*  Unexpected reply */

		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;

	}

	if ( up->iTimestampCount == 2 ) {
		/* Process date and time */

		if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0]
		  && up->iTimestamp[0]     <= up->iTimestamp[1] ) {
			/* 3 commands (time,date,stim) was executed in two seconds */
			jjy_synctime( peer, pp, up ) ;
			return JJY_RECEIVE_DONE ;
		} else if ( up->iTimestamp[0] > up->iTimestamp[1] ) {
			/* Over midnight, and date is unsure */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2,
				  up->iTimestamp[0], up->iTimestamp[1] ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
			return JJY_RECEIVE_SKIP ;
		} else {
			/* Slow reply */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2,
				  up->iTimestamp[0], up->iTimestamp[1] ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

	}

	/* Issue next command */

	if ( tristate_jjy01_command_sequence[up->iCommandSeq].command != NULL ) {
		up->iCommandSeq ++ ;
	}

	if ( tristate_jjy01_command_sequence[up->iCommandSeq].command == NULL ) {
		/* Command sequence completed */
		return JJY_RECEIVE_DONE ;
	}

	pCmd =  tristate_jjy01_command_sequence[up->iCommandSeq].command ;
	iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ;
	if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_tristate_jjy01  ( int unit, struct peer *peer )
{
#ifdef DEBUG
	static const char *sFunctionName = "jjy_poll_tristate_jjy01" ;
#endif

	UNUSED_ARG(unit);

	struct refclockproc *pp ;
	struct jjyunit	    *up ;

	const char *pCmd ;
	int 	iCmdLen ;

	pp = peer->procptr;
	up = pp->unitptr ;

	up->bLineError = false ;
	up->iTimestampCount = 0 ;

	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
		/* Skip "dcst" and "stus" commands */
		up->iCommandSeq = 2 ;
		up->iLineCount = 2 ;
	}

	DPRINT(1, ("%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X "
		   "up->iLineCount=%d\n",
		   sFunctionName, pp->sloppyclockflag,
		   (unsigned)CLK_FLAG1,
		   up->iLineCount )) ;

	/*
	 * Send a first command
	 */

	up->iCommandSeq ++ ;

	pCmd =  tristate_jjy01_command_sequence[up->iCommandSeq].command ;
	iCmdLen = tristate_jjy01_command_sequence[up->iCommandSeq].commandLength ;
	if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The C-DEX Co. Ltd. JJY receiver JST2000							##*/
/*##												##*/
/*##    refclock jjy unit X subtype 2								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  <ENQ>1J<ETX>          <STX>JYYMMDD HHMMSSS<ETX>                 J is a fixed character        */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak cdex_jst2000_raw_break [ ] =
{
	{ "\x03", 1 }, { NULL, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_cdex_jst2000 ( int unit, struct peer *peer, struct jjyunit *up )
{

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: C-DEX Co. Ltd. JST2000" ) ;

	UNUSED_ARG(unit);

	up->unittype  = UNITTYPE_CDEX_JST2000 ;
	up->linespeed = SPEED232_CDEX_JST2000 ;
	up->linediscipline = LDISC_RAW ;

	up->pRawBreak = cdex_jst2000_raw_break ;
	up->bWaitBreakString = true ;

	up->bSkipCntrlCharOnly = false ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
{

	struct jjyunit      *up ;
	struct refclockproc *pp ;
	struct peer         *peer ;

	char	*pBuf, sLog [ MAX_LOGTEXT ] ;
	int 	iLen ;
	int 	rc ;

	/* Initialize pointers */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_cdex_jst2000", iLen ) ;

	/* Check expected reply */

	if ( up->iCommandSeq != 1 ) {
		/* Command sequence has not been started, or has been completed */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* Wait until ETX comes */

	if ( up->iLineBufLen < 17 || up->sLineBuf[up->iLineBufLen-1] != 0x03 ) {
		return JJY_RECEIVE_UNPROCESS ;
	}

	/* Check reply length */

	if ( iLen != 15 ) {
		/* Unexpected reply length */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* JYYMMDDWHHMMSSS */

	rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
		      &up->year, &up->month, &up->day,
		      &up->hour, &up->minute, &up->second,
		      &up->msecond ) ;

	if ( rc != 7 || up->month < 1 || up->month > 12 ||
	     up->day < 1 || up->day > 31 || up->hour > 23 ||
	     up->minute > 59 || up->second > 60 ) {
		/* Invalid date and time */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
			  rc, up->year, up->month, up->day,
			  up->hour, up->minute, up->second ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	up->year    += 2000 ;
	up->msecond *= 100 ;

	jjy_synctime( peer, pp, up ) ;

	return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
{

	struct refclockproc *pp ;
	struct jjyunit      *up ;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	up->bLineError = false ;
	up->iRawBufLen = 0 ;
	up->iLineBufLen = 0 ;
	up->iTextBufLen = 0 ;

	/*
	 * Send "<ENQ>1J<ETX>" command
	 */

	up->iCommandSeq ++ ;

	if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4  ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\0051J\003" ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The Echo Keisokuki Co. Ltd. JJY receiver LT2000						##*/
/*##												##*/
/*##    refclock jjy unit X subtype 3								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  #                                                               Mode 1 ( Request & Send )     */
/*  T                     YYMMDDWHHMMSS<BCC1><BCC2><CR>                                           */
/*  C                                                               Mode 2 ( Continuous )         */
/*                        YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR>     0.5 sec before time stamp     */
/*                        <SUB>                                     Second signal                 */
/*                                                                                                */
/*################################################################################################*/

#define	ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND		1
#define	ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS		2
#define	ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS	3

#define	ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND 	"#"
/* #define	ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_TIME  "T" UNUSED */
/* #define	ECHOKEISOKUKI_LT2000_COMMAND_CONTINUOUS  "C" UNUSED */

/**************************************************************************************************/

static int
jjy_start_echokeisokuki_lt2000 ( int unit, struct peer *peer, struct jjyunit *up )
{
	UNUSED_ARG(unit);

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Echo Keisokuki Co. Ltd. LT2000" ) ;

	up->unittype  = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
	up->linespeed = SPEED232_ECHOKEISOKUKI_LT2000 ;
	up->linediscipline = LDISC_STD ;

	up->operationmode = ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
{

	struct jjyunit      *up ;
	struct refclockproc *pp ;
	struct peer	    *peer;

	char	*pBuf, sLog [ MAX_LOGTEXT ], sErr [ 60 ] ;
	int 	iLen ;
	int 	rc ;
	int	i, ibcc, ibcc1, ibcc2 ;

	/* Initialize pointers */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_echokeisokuki_lt2000", iLen ) ;

	/* Check reply length */

	if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND
	       && iLen != 15 )
	  || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
	       && iLen != 17 )
	  || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS
	       && iLen != 17 ) ) {
		/* Unexpected reply length */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND && iLen == 15 ) {
		/* YYMMDDWHHMMSS<BCC1><BCC2> */

		for ( i = ibcc = 0 ; i < 13 ; i ++ ) {
			ibcc ^= pBuf[i] ;
		}

		ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ;
		ibcc2 = 0x30 | ( ( ibcc      ) & 0xF ) ;
		if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) {
			snprintf(sErr, sizeof(sErr),
                               " BCC error : Recv=%02X,%02X / Calc=%02X,%02X ",
			       (unsigned)(pBuf[13] & 0xFF),
                               (unsigned)(pBuf[14] & 0xFF),
			       (unsigned)ibcc1, (unsigned)ibcc2);
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
				  sErr ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

	}

	if ( ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND
	       && iLen == 15 )
	  || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
	       && iLen == 17 )
	  || ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS
	       && iLen == 17 ) ) {
		/* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */

		rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d",
			      &up->year, &up->month, &up->day,
			      &up->hour, &up->minute, &up->second ) ;

		if ( rc != 6 || up->month < 1 || up->month > 12
		  || up->day < 1 || up->day > 31
		  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid date and time */
			snprintf( sLog, sizeof(sLog),
                                 JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
				 rc, up->year, up->month, up->day,
				 up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		up->year += 2000 ;

		if ( up->operationmode == ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS
		  || up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) {
			/* A time stamp comes on every 0.5 second in the mode 2 of the LT-2000. */

			up->msecond = 500 ;
			up->second -- ;
			if ( up->second < 0 ) {
				up->second = 59 ;
				up->minute -- ;
				if ( up->minute < 0 ) {
					up->minute = 59 ;
					up->hour -- ;
					if ( up->hour < 0 ) {
						up->hour = 23 ;
						up->day -- ;
						if ( up->day < 1 ) {
							up->month -- ;
							if ( up->month < 1 ) {
								up->month = 12 ;
								up->year -- ;
							}
						}
					}
				}
			}

		}

		jjy_synctime( peer, pp, up ) ;


	}

	if (up->operationmode == ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS ) {
	    /* Switch from mode 2 to mode 1 in order to restraint of
             * useless time stamp.
             */
	    iLen = (int)strlen( ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ;
	    if ( write ( pp->io.fd, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND,
                         (size_t)iLen ) != iLen  ) {
		    refclock_report ( peer, CEVNT_FAULT ) ;
	    }

	    jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, ECHOKEISOKUKI_LT2000_COMMAND_REQUEST_SEND ) ;

	}

	return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer )
{

	struct refclockproc *pp ;
	struct jjyunit      *up ;

	char	sCmd[2] ;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	up->bLineError = false ;

	/*
	 * Send "T" or "C" command
	 */

	switch ( up->operationmode ) {
	case ECHOKEISOKUKI_LT2000_MODE_REQUEST_SEND :
		sCmd[0] = 'T' ;
		break ;
	case ECHOKEISOKUKI_LT2000_MODE_CONTINUOUS :
	case ECHOKEISOKUKI_LT2000_MODE_SWITCHING_CONTINUOUS :
		sCmd[0] = 'C' ;
		break ;
        default:
		/* huh? */
		break ;
	}
	sCmd[1] = 0 ;

	if ( write ( pp->io.fd, sCmd, 1 ) != 1  ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The CITIZEN T.I.C CO., LTD. JJY receiver JJY200						##*/
/*##												##*/
/*##    refclock jjy unit X subtype 4								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*                        'XX YY/MM/DD W HH:MM:SS<CR>               XX:OK|NG|ER  W:0(Mon)-6(Sun)  */
/*                                                                                                */
/*################################################################################################*/

static int
jjy_start_citizentic_jjy200 ( int unit, struct peer *peer, struct jjyunit *up )
{
	UNUSED_ARG(unit);

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: CITIZEN T.I.C CO. LTD. JJY200" ) ;

	up->unittype  = UNITTYPE_CITIZENTIC_JJY200 ;
	up->linespeed = SPEED232_CITIZENTIC_JJY200 ;
	up->linediscipline = LDISC_STD ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp )
{

	struct jjyunit		*up ;
	struct refclockproc	*pp ;
	struct peer		*peer;

	char	*pBuf, sLog [ MAX_LOGTEXT ], sMsg [ 16 ] ;
	int	iLen ;
	char	cApostrophe, sStatus[3] ;
	int	iWeekday ;

	/* Initialize pointers */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_citizentic_jjy200", iLen ) ;

	/*
	 * JJY-200 sends a timestamp every second.
	 * So, a timestamp is ignored unless it is right after polled.
	 */

	if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) {
		return JJY_RECEIVE_SKIP ;
	}

	/* Check reply length */

	if ( iLen != 23 ) {
		/* Unexpected reply length */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* 'XX YY/MM/DD W HH:MM:SS<CR> */

	int rc = sscanf ( pBuf, "%c%2s %2d/%2d/%2d %1d %2d:%2d:%2d",
			  &cApostrophe, sStatus,
			  &up->year, &up->month, &up->day, &iWeekday,
			  &up->hour, &up->minute, &up->second ) ;
	sStatus[2] = 0 ;

	if ( rc != 9 || cApostrophe != '\''
	  || ( strcmp( sStatus, "OK" ) != 0
	    && strcmp( sStatus, "NG" ) != 0
	    && strcmp( sStatus, "ER" ) != 0 )
	  || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
	  || iWeekday > 6
	  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
		/* Invalid date and time */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
			  rc, up->year, up->month, up->day,
			  up->hour, up->minute, up->second ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	} else if ( strcmp( sStatus, "NG" ) == 0
		 || strcmp( sStatus, "ER" ) == 0 ) {
		/* Timestamp is unsure */
		snprintf( sMsg, sizeof(sMsg), "status=%s", sStatus ) ;
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_TIMESTAMP_UNSURE,
			  sMsg ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;
		return JJY_RECEIVE_SKIP ;
	}

	up->year += 2000 ;
	up->msecond = 0 ;

	jjy_synctime( peer, pp, up ) ;

	return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_citizentic_jjy200 ( int unit, struct peer *peer )
{
	UNUSED_ARG(unit);

	struct refclockproc *pp ;
	struct jjyunit	    *up ;

	pp = peer->procptr ;
	up = pp->unitptr ;

	up->bLineError = false ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The Tristate Ltd. GPS clock TS-GPS01							##*/
/*##												##*/
/*##    refclock jjy unit X subtype 5								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  This clock has NMEA mode and command/response mode.                                           */
/*  When this jjy driver are used, set to command/response mode of this clock                     */
/*  by the onboard switch SW4, and make sure the LED-Y is tured on.                               */
/*  Other than this JJY driver, the refclock driver type 20, generic NMEA driver,                 */
/*  works with the NMEA mode of this clock.                                                       */
/*                                                                                                */
/*  Command               Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  stus<CR><LF>          *R|*G|*U|+U<CR><LF>                                                     */
/*  date<CR><LF>          YY/MM/DD<CR><LF>                                                        */
/*  time<CR><LF>          HH:MM:SS<CR><LF>                                                        */
/*                                                                                                */
/*################################################################################################*/

#define	TS_GPS01_COMMAND_NUMBER_DATE	1
#define	TS_GPS01_COMMAND_NUMBER_TIME	2
#define	TS_GPS01_COMMAND_NUMBER_STUS	4

/* #define	TS_GPS01_REPLY_DATE		"yyyy/mm/dd" UNUSED */
/* #define	TS_GPS01_REPLY_TIME		"hh:mm:ss"   UNUSED */
#define	TS_GPS01_REPLY_STUS_RTC		"*R"
#define	TS_GPS01_REPLY_STUS_GPS		"*G"
#define	TS_GPS01_REPLY_STUS_UTC		"*U"
#define	TS_GPS01_REPLY_STUS_PPS		"+U"

#define	TS_GPS01_REPLY_LENGTH_DATE	    10	/* Length without <CR><LF> */
#define	TS_GPS01_REPLY_LENGTH_TIME	    8	/* Length without <CR><LF> */
#define	TS_GPS01_REPLY_LENGTH_STUS	    2	/* Length without <CR><LF> */

static  struct
{
	char	commandNumber ;
	const char	*command ;
	int	commandLength ;
	int	iExpectedReplyLength ;
} tristate_gps01_command_sequence[] =
{
	{ 0, NULL, 0, 0 }, /* Idle */
	{ TS_GPS01_COMMAND_NUMBER_STUS, "stus\r\n", 6, TS_GPS01_REPLY_LENGTH_STUS },
	{ TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME },
	{ TS_GPS01_COMMAND_NUMBER_DATE, "date\r\n", 6, TS_GPS01_REPLY_LENGTH_DATE },
	{ TS_GPS01_COMMAND_NUMBER_TIME, "time\r\n", 6, TS_GPS01_REPLY_LENGTH_TIME },
	/* End of command */
	{ 0, NULL, 0, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_tristate_gpsclock01 ( int unit, struct peer *peer, struct jjyunit *up )
{
	UNUSED_ARG(unit);

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Tristate Ltd. TS-GPS01" ) ;

	up->unittype  = UNITTYPE_TRISTATE_GPSCLOCK01 ;
	up->linespeed = SPEED232_TRISTATE_GPSCLOCK01 ;
	up->linediscipline = LDISC_STD ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_tristate_gpsclock01 ( struct recvbuf *rbufp )
{
#ifdef DEBUG
	static	const char	*sFunctionName = "jjy_receive_tristate_gpsclock01" ;
#endif

	struct jjyunit	    *up ;
	struct refclockproc *pp ;
	struct peer	    *peer;

	char	*pBuf, sLog [ MAX_LOGTEXT ] ;
	int 	iLen ;
	int 	rc ;

	const char	*pCmd ;
	int 	iCmdLen ;

	/* Initialize pointers */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_tristate_gpsclock01", iLen ) ;

	/* Ignore NMEA data stream */

	if ( iLen > 5
	  && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
	        DPRINT(1, ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
			    sFunctionName, pBuf )) ;
		return JJY_RECEIVE_WAIT ;
	}

	/*
	 * Skip command prompt '$Cmd>' from the TS-GPSclock-01
	 */
	if ( iLen == 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
		return JJY_RECEIVE_WAIT ;
	} else if ( iLen > 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
		pBuf += 5 ;
		iLen -= 5 ;
	}

	/*
	 * Ignore NMEA data stream after command prompt
	 */
	if ( iLen > 5
	  && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
	        DPRINT(1, ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
			    sFunctionName, pBuf )) ;
		return JJY_RECEIVE_WAIT ;
	}

	/* Check expected reply */

	if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
		/* Command sequence has not been started, or has been completed */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* Check reply length */

	if ( iLen != tristate_gps01_command_sequence[up->iCommandSeq].iExpectedReplyLength ) {
		/* Unexpected reply length */
		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;
	}

	/* Parse reply */

	switch ( tristate_gps01_command_sequence[up->iCommandSeq].commandNumber ) {

	case TS_GPS01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD */

		rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;

		if ( rc != 3 || up->year < 2000 || 2099 <= up->year
		  || up->month < 1 || 12 < up->month
		  || up->day < 1 || 31 < up->day ) {
			/* Invalid date */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
				  rc, up->year, up->month, up->day ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	case TS_GPS01_COMMAND_NUMBER_TIME : /* HH:MM:SS */

		if ( up->iTimestampCount >= 2 ) {
			/* Too many time reply */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_TOO_MANY_REPLY,
				  up->iTimestampCount ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		rc = sscanf ( pBuf, "%2d:%2d:%2d",
			      &up->hour, &up->minute, &up->second ) ;

		if ( rc != 3
		  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid time */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
				  rc, up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

		up->iTimestampCount++ ;

		up->msecond = 0 ;

		break ;

	case TS_GPS01_COMMAND_NUMBER_STUS :

		if ( strncmp( pBuf, TS_GPS01_REPLY_STUS_RTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0
		  || strncmp( pBuf, TS_GPS01_REPLY_STUS_GPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0
		  || strncmp( pBuf, TS_GPS01_REPLY_STUS_UTC, TS_GPS01_REPLY_LENGTH_STUS ) == 0
		  || strncmp( pBuf, TS_GPS01_REPLY_STUS_PPS, TS_GPS01_REPLY_LENGTH_STUS ) == 0 ) {
			/* Good */
		} else {
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
				  pBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	default : /*  Unexpected reply */

		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;

	}

	if ( up->iTimestampCount == 2 ) {
		/* Process date and time */

		if ( up->iTimestamp[1] - 2 <= up->iTimestamp[0]
		  && up->iTimestamp[0]     <= up->iTimestamp[1] ) {
			/* 3 commands (time,date,stim) was executed in two seconds */
			jjy_synctime( peer, pp, up ) ;
			return JJY_RECEIVE_DONE ;
		} else if ( up->iTimestamp[0] > up->iTimestamp[1] ) {
			/* Over midnight, and date is unsure */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_OVER_MIDNIGHT_2,
				  up->iTimestamp[0], up->iTimestamp[1] ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
			return JJY_RECEIVE_SKIP ;
		} else {
			/* Slow reply */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SLOW_REPLY_2,
				  up->iTimestamp[0], up->iTimestamp[1] ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

	}

	if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
		/* Command sequence completed */
		jjy_synctime( peer, pp, up ) ;
		return JJY_RECEIVE_DONE ;
	}

	/* Issue next command */

	if ( tristate_gps01_command_sequence[up->iCommandSeq].command != NULL ) {
		up->iCommandSeq ++ ;
	}

	if ( tristate_gps01_command_sequence[up->iCommandSeq].command == NULL ) {
		/* Command sequence completed */
		up->iProcessState = JJY_PROCESS_STATE_DONE ;
		return JJY_RECEIVE_DONE ;
	}

	pCmd =  tristate_gps01_command_sequence[up->iCommandSeq].command ;
	iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ;
	if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_tristate_gpsclock01 ( int unit, struct peer *peer )
{
#ifdef DEBUG
	static const char *sFunctionName = "jjy_poll_tristate_gpsclock01" ;
#endif

	struct refclockproc *pp ;
	struct jjyunit	    *up ;

	const char	*pCmd ;
	int 	iCmdLen ;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	up->iTimestampCount = 0 ;

	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
		/* Skip "stus" command */
		up->iCommandSeq = 1 ;
		up->iLineCount = 1 ;
	}

        DPRINT(1, ("%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X "
		   "up->iLineCount=%d\n",
		   sFunctionName, pp->sloppyclockflag,
		   (unsigned)CLK_FLAG1,
		   up->iLineCount )) ;

	/*
	 * Send a first command
	 */

	up->iCommandSeq ++ ;

	pCmd =  tristate_gps01_command_sequence[up->iCommandSeq].command ;
	iCmdLen = tristate_gps01_command_sequence[up->iCommandSeq].commandLength ;
	if ( write ( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report ( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    The SEIKO TIME SYSTEMS TDC-300								##*/
/*##												##*/
/*##    refclock jjy unit X subtype 6								##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Type                  Response                                  Remarks                       */
/*  --------------------  ----------------------------------------  ----------------------------  */
/*  Type 1                <STX>HH:MM:SS<ETX>                                                      */
/*  Type 2                <STX>YYMMDDHHMMSSWLSCU<ETX>               W:0(Sun)-6(Sat)               */
/*  Type 3                <STX>YYMMDDWHHMMSS<ETX>                   W:0(Sun)-6(Sat)               */
/*                        <STX><xE5><ETX>                           5 to 10 mSec. before second   */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak seiko_tsys_tdc_300_raw_break [ ] =
{
	{ "\x03", 1 }, { NULL, 0 }
} ;

/**************************************************************************************************/

static int
jjy_start_seiko_tsys_tdc_300 ( int unit, struct peer *peer, struct jjyunit *up )
{
	UNUSED_ARG(unit);

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: SEIKO TIME SYSTEMS TDC-300" ) ;

	up->unittype  = UNITTYPE_SEIKO_TIMESYS_TDC_300 ;
	up->linespeed = SPEED232_SEIKO_TIMESYS_TDC_300 ;
	up->linediscipline = LDISC_RAW ;

	up->pRawBreak = seiko_tsys_tdc_300_raw_break ;
	up->bWaitBreakString = true ;

	up->bSkipCntrlCharOnly = false ;

	return 0 ;

}

/**************************************************************************************************/

static int
jjy_receive_seiko_tsys_tdc_300 ( struct recvbuf *rbufp )
{

	struct peer		*peer;
	struct refclockproc	*pp ;
	struct jjyunit		*up ;

	char	*pBuf, sLog [ MAX_LOGTEXT ] ;
	int	iLen, i ;
	int	rc, iWeekday ;
	time_t	now ;
	struct	tm	*pTime ;
	struct	tm	tmbuf;

	/* Initialize pointers */

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	DEBUG_PRINTF_JJY_RECEIVE( "jjy_receive_seiko_tsys_tdc_300", iLen ) ;

	/*
	 * TDC-300 sends a timestamp every second.
	 * So, a timestamp is ignored unless it is right after polled.
	 */

	if ( up->iProcessState != JJY_PROCESS_STATE_RECEIVE ) {
		return JJY_RECEIVE_SKIP ;
	}

	/* Process timestamp */

	up->iReceiveSeq ++ ;

	switch ( iLen ) {

	case 8 : /* Type 1 : <STX>HH:MM:SS<ETX> */

		for ( i = 0 ; i < iLen ; i ++ ) {
			pBuf[i] &= 0x7F ;
		}

		rc = sscanf ( pBuf+1, "%2d:%2d:%2d",
		      &up->hour, &up->minute, &up->second ) ;

		if ( rc != 3
		  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid time */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
				  rc, up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		} else if ( up->hour == 23 && up->minute == 59 && up->second >= 55 ) {
			/* Uncertainty date guard */
			return JJY_RECEIVE_WAIT ;
		}

		time( &now ) ;
		if ((pTime = localtime_r( &now, &tmbuf )) != NULL) {
			up->year  = pTime->tm_year ;
			up->month = pTime->tm_mon + 1 ;
			up->day   = pTime->tm_mday ;
		}

		break ;

	case 17 : /* Type 2 : <STX>YYMMDDHHMMSSWLSCU<ETX> */

		for ( i = 0 ; i < iLen ; i ++ ) {
			pBuf[i] &= 0x7F ;
		}

		rc = sscanf ( pBuf+1, "%2d%2d%2d%2d%2d%2d%1d",
		      &up->year, &up->month, &up->day,
		      &up->hour, &up->minute, &up->second, &iWeekday ) ;

		if ( rc != 7
		  || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
		  || iWeekday > 6
		  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid date and time */
			snprintf(sLog, sizeof(sLog),
                                 JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
				 rc, up->year, up->month, up->day,
				 up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	case 13 : /* Type 3 : <STX>YYMMDDWHHMMSS<ETX> */

		rc = sscanf ( pBuf, "%2d%2d%2d%1d%2d%2d%2d",
		      &up->year, &up->month, &up->day, &iWeekday,
		      &up->hour, &up->minute, &up->second ) ;

		if ( rc != 7
		  || up->month < 1 || up->month > 12 || up->day < 1 || up->day > 31
		  || iWeekday > 6
		  || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid date and time */
			snprintf( sLog, sizeof(sLog),
                                 JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATETIME,
				 rc, up->year, up->month, up->day,
				 up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		return JJY_RECEIVE_WAIT ;

	case 1 : /* Type 3 : <STX><xE5><ETX> */

		if ( ( *pBuf & 0xFF ) != 0xE5 ) {
			/* Invalid second signal */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_INVALID_REPLY,
				  up->sLineBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		} else if ( up->iReceiveSeq == 1 ) {
			/* Wait for next timestamp */
			up->iReceiveSeq -- ;
			return JJY_RECEIVE_WAIT ;
		} else if ( up->iReceiveSeq >= 3 ) {
			/* Unexpected second signal */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
				  up->sLineBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
			return JJY_RECEIVE_ERROR ;
		}

		break ;

	default : /* Unexpected reply length */

		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_INVALID_LENGTH,
			  iLen ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
		up->bLineError = true ;
		return JJY_RECEIVE_ERROR ;

	}

	up->year += 2000 ;
	up->msecond = 0 ;

	jjy_synctime( peer, pp, up ) ;

	return JJY_RECEIVE_DONE ;

}

/**************************************************************************************************/

static void
jjy_poll_seiko_tsys_tdc_300 ( int unit, struct peer *peer )
{
	UNUSED_ARG(unit);

	struct refclockproc *pp ;
	struct jjyunit	    *up ;

	pp = peer->procptr ;
	up = pp->unitptr ;

	up->bLineError = false ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    Telephone JJY										##*/
/*##												##*/
/*##    refclock jjy unit X subtype 100 to 180							##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/
/*                                                                                                */
/*  Prompt                Command               Response              Remarks                     */
/*  --------------------  --------------------  --------------------  --------------------------  */
/*  Name<SP>?<SP>         TJJY<CR>              Welcome messages      TJJY is a guest user ID     */
/*  >                     4DATE<CR>             YYYYMMDD<CR>                                      */
/*  >                     LEAPSEC<CR>           XX<CR>                One of <SP>0, +1, -1        */
/*  >                     TIME<CR>              HHMMSS<CR>            3 times on second           */
/*  >                     BYE<CR>               Sayounara messages                                */
/*                                                                                                */
/*################################################################################################*/

static struct jjyRawDataBreak teljjy_raw_break [ ] =
{
	{ "\r\n", 2 },
	{ "\r"  , 1 },
	{ "\n"  , 1 },
	{ "Name ? ", 7 },
	{ ">"   , 1 },
	{ "+++" , 3 },
	{ NULL  , 0 }
} ;

#define	TELJJY_STATE_IDLE	0
#define	TELJJY_STATE_DAILOUT	1
#define	TELJJY_STATE_LOGIN	2
#define	TELJJY_STATE_CONNECT	3
#define	TELJJY_STATE_BYE	4

#define	TELJJY_EVENT_NULL	0
#define	TELJJY_EVENT_START	1
#define	TELJJY_EVENT_CONNECT	2
#define	TELJJY_EVENT_DISCONNECT	3
#define	TELJJY_EVENT_COMMAND	4
#define	TELJJY_EVENT_LOGIN	5	/* Posted by the jjy_receive_telephone */
#define	TELJJY_EVENT_PROMPT	6	/* Posted by the jjy_receive_telephone */
#define	TELJJY_EVENT_DATA	7	/* Posted by the jjy_receive_telephone */
#define	TELJJY_EVENT_ERROR	8	/* Posted by the jjy_receive_telephone */
#define	TELJJY_EVENT_SILENT	9	/* Posted by the jjy_timer_telephone */
#define	TELJJY_EVENT_TIMEOUT	10	/* Posted by the jjy_timer_telephone */

static	void 	teljjy_control		( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;

static	int 	teljjy_idle_ignore	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_idle_dialout	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_dial_ignore	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_dial_login	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_dial_disc	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_ignore	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_disc	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_conn	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_login	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_silent	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_login_error	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_ignore	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_disc	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_send	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_data	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_silent	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_conn_error	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_bye_ignore	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_bye_disc 	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;
static	int 	teljjy_bye_modem	( struct peer *peer, struct refclockproc *, struct jjyunit * ) ;

static int ( *pTeljjyHandler [ ] [ 5 ] ) (struct peer *, struct refclockproc *, struct jjyunit *) =
{               	/*STATE_IDLE           STATE_DAILOUT       STATE_LOGIN           STATE_CONNECT       STATE_BYE        */
/* NULL       */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* START      */	{ teljjy_idle_dialout, teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* CONNECT    */	{ teljjy_idle_ignore , teljjy_dial_login , teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_ignore },
/* DISCONNECT */	{ teljjy_idle_ignore , teljjy_dial_disc  , teljjy_login_disc  , teljjy_conn_disc  , teljjy_bye_disc   },
/* COMMAND    */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_ignore, teljjy_bye_modem  },
/* LOGIN      */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_login , teljjy_conn_error , teljjy_bye_ignore },
/* PROMPT     */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_conn  , teljjy_conn_send  , teljjy_bye_ignore },
/* DATA       */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_ignore, teljjy_conn_data  , teljjy_bye_ignore },
/* ERROR      */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_error , teljjy_conn_error , teljjy_bye_ignore },
/* SILENT     */	{ teljjy_idle_ignore , teljjy_dial_ignore, teljjy_login_silent, teljjy_conn_silent, teljjy_bye_ignore },
/* TIMEOUT    */	{ teljjy_idle_ignore , teljjy_dial_disc  , teljjy_login_error , teljjy_conn_error , teljjy_bye_modem  }
} ;

static short iTeljjyNextState [ ] [ 5 ] =
{               	/*STATE_IDLE            STATE_DAILOUT         STATE_LOGIN           STATE_CONNECT         STATE_BYE         */
/* NULL       */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* START      */	{ TELJJY_STATE_DAILOUT, TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* CONNECT    */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_LOGIN  , TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* DISCONNECT */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE },
/* COMMAND    */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* LOGIN      */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* PROMPT     */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_CONNECT, TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* DATA       */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* ERROR      */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_BYE    , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  },
/* SILENT     */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_DAILOUT, TELJJY_STATE_LOGIN  , TELJJY_STATE_CONNECT, TELJJY_STATE_BYE  },
/* TIMEOUT    */	{ TELJJY_STATE_IDLE   , TELJJY_STATE_IDLE   , TELJJY_STATE_BYE    , TELJJY_STATE_BYE    , TELJJY_STATE_BYE  }
} ;

static short iTeljjyPostEvent [ ] [ 5 ] =
{               	/*STATE_IDLE         STATE_DAILOUT      STATE_LOGIN           STATE_CONNECT         STATE_BYE         */
/* NULL       */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* START      */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* CONNECT    */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* DISCONNECT */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* COMMAND    */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* LOGIN      */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* PROMPT     */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_PROMPT , TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* DATA       */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* ERROR      */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL },
/* SILENT     */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL   , TELJJY_EVENT_NULL },
/* TIMEOUT    */	{ TELJJY_EVENT_NULL, TELJJY_EVENT_NULL, TELJJY_EVENT_COMMAND, TELJJY_EVENT_COMMAND, TELJJY_EVENT_NULL }
} ;

static short iTeljjySilentTimeout [ 5 ] = { 0,   0, 10,  5,  0 } ;
static short iTeljjyStateTimeout  [ 5 ] = { 0, 120, 60, 60, 40 } ;

#define	TELJJY_STAY_CLOCK_STATE  	0
#define	TELJJY_CHANGE_CLOCK_STATE	1

/* Command and replay */

#define	TELJJY_REPLY_NONE	0
#define	TELJJY_REPLY_4DATE	1
#define	TELJJY_REPLY_TIME	2
#define	TELJJY_REPLY_LEAPSEC	3
#define	TELJJY_REPLY_LOOP	4
/* #define	TELJJY_REPLY_PROMPT 5 UNUSED */
#define	TELJJY_REPLY_LOOPBACK	6
#define	TELJJY_REPLY_COM	7

#define	TELJJY_COMMAND_START_SKIP_LOOPBACK	7

static  struct
{
	const char	*command ;
	int	commandLength ;
	int	iEchobackReplyLength ;
	int	iExpectedReplyType   ;
	int	iExpectedReplyLength ;
} teljjy_command_sequence[] =
{
	{ NULL, 0, 0, 0, 0 }, /* Idle */
	{ "LOOP\r"   , 5, 4, TELJJY_REPLY_LOOP    , 0 }, /* Getting into loopback mode */
	{ ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
	{ ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
	{ ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
	{ ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
	{ ">"        , 1, 1, TELJJY_REPLY_LOOPBACK, 0 }, /* Loopback measuring of delay time */
	{ "COM\r"    , 4, 3, TELJJY_REPLY_COM     , 0 }, /* Exit from loopback mode */
	/* TELJJY_COMMAND_START_SKIP_LOOPBACK */
	{ "TIME\r"   , 5, 4, TELJJY_REPLY_TIME    , 6 },
	{ "4DATE\r"  , 6, 5, TELJJY_REPLY_4DATE   , 8 },
	{ "LEAPSEC\r", 8, 7, TELJJY_REPLY_LEAPSEC , 2 },
	{ "TIME\r"   , 5, 4, TELJJY_REPLY_TIME    , 6 },
	{ "BYE\r"    , 4, 3, TELJJY_REPLY_NONE    , 0 },
	/* End of command */
	{ NULL, 0, 0, 0, 0 }
} ;

#define	TELJJY_LOOPBACK_DELAY_THRESHOLD		700 /* Milli second */

#ifdef	DEBUG
#define	DEBUG_TELJJY_PRINTF(sFunc)	{ if ( debug ) { printf ( "refclock_jjy.c : %s : iClockState=%d iClockEvent=%d iTeljjySilentTimer=%d iTeljjyStateTimer=%d iClockCommandSeq=%d\n", sFunc, up->iClockState, up->iClockEvent, up->iTeljjySilentTimer, up->iTeljjyStateTimer, up->iClockCommandSeq ) ; } }
#else
#define	DEBUG_TELJJY_PRINTF(sFunc)
#endif

/**************************************************************************************************/

static bool
jjy_start_telephone ( int unit, struct peer *peer, struct jjyunit *up )
{

	char	sLog [ MAX_LOGTEXT ], sFirstThreeDigits [ 4 ] ;
	int	iNumberOfDigitsOfPhoneNumber, iCommaCount, iCommaPosition ;
	size_t	i;
	int	iFirstThreeDigitsCount ;

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, "Refclock: Telephone JJY" ) ;

	UNUSED_ARG(unit);

	up->unittype  = UNITTYPE_TELEPHONE ;
	up->linespeed = SPEED232_TELEPHONE ;
	up->linediscipline = LDISC_RAW ;

	up->pRawBreak = teljjy_raw_break ;
	up->bWaitBreakString = true ;

	up->bSkipCntrlCharOnly = true ;

	up->iClockState = TELJJY_STATE_IDLE ;
	up->iClockEvent = TELJJY_EVENT_NULL ;

	/* Check the telephone number */

	if ( sys_phone[0] == NULL ) {
		msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf must be specified." ) ;
		up->bInitError = true ;
		return true ;
	}

	if ( sys_phone[1] != NULL ) {
		msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be only one." ) ;
		up->bInitError = true ;
		return true ;
	}

	iNumberOfDigitsOfPhoneNumber = iCommaCount = iCommaPosition = iFirstThreeDigitsCount = 0 ;
	for ( i = 0 ; i < strlen( sys_phone[0] ) ; i ++ ) {
		if ( isdigit( (int)*(sys_phone[0]+i) ) ) {
		    if ( iFirstThreeDigitsCount < (int)sizeof(sFirstThreeDigits)-1 ) {
				sFirstThreeDigits[iFirstThreeDigitsCount++] = *(sys_phone[0]+i) ;
			}
			iNumberOfDigitsOfPhoneNumber ++ ;
		} else if ( *(sys_phone[0]+i) == ',' ) {
			iCommaCount ++ ;
			if ( iCommaCount > 1 ) {
				msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be zero or one comma." ) ;
				up->bInitError = true ;
				return true ;
			}
			iFirstThreeDigitsCount = 0 ;
			iCommaPosition = (int)i ;
		} else if ( *(sys_phone[0]+i) != '-' ) {
			msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone in the ntpd.conf should be a number or a hyphen." ) ;
			up->bInitError = true ;
			return true ;
		}
	}
	sFirstThreeDigits[iFirstThreeDigitsCount] = 0 ;

	if ( iCommaCount == 1 ) {
		if ( iCommaPosition != 1 || *sys_phone[0] != '0' ) {
			msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : Getting an outside line should be '0,'." ) ;
			up->bInitError = true ;
			return true ;
		}
	}

	if ( iNumberOfDigitsOfPhoneNumber - iCommaPosition < 6 || 10 < iNumberOfDigitsOfPhoneNumber - iCommaPosition ) {
		/* Too short or too long */
		msyslog( LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone=%s : Number of digits should be 6 to 10.", sys_phone[0] ) ;
		up->bInitError = true ;
		return true ;
	}

	if ( strncmp( sFirstThreeDigits + iCommaPosition, "00" , 2 ) == 0
	  || strncmp( sFirstThreeDigits + iCommaPosition, "10" , 2 ) == 0
	  || strncmp( sFirstThreeDigits + iCommaPosition, "11" , 2 ) == 0
	  || strncmp( sFirstThreeDigits + iCommaPosition, "12" , 2 ) == 0
	  || strncmp( sFirstThreeDigits + iCommaPosition, "171", 3 ) == 0
	  || strncmp( sFirstThreeDigits + iCommaPosition, "177", 3 ) == 0
	  || ( sFirstThreeDigits[0] == '0' &&  sFirstThreeDigits[2] == '0' ) ) {
		/* Not allowed because of emergency numbers or special service numbers */
		msyslog(LOG_ERR, "REFCLOCK: refclock_jjy.c : jjy_start_telephone : phone=%s : First 2 or 3 digits are not allowed.", sys_phone[0] ) ;
		up->bInitError = true ;
		return true ;
	}

	snprintf( sLog, sizeof(sLog), "phone=%s", sys_phone[0] ) ;
	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;

	if ( peer->cfg.minpoll < 8 ) {
		/* minpoll must be greater or equal to 8 ( 256 seconds = about 4 minutes ) */
		int oldminpoll = peer->cfg.minpoll ;
		peer->cfg.minpoll = 8 ;
		if ( peer->ppoll < peer->cfg.minpoll ) {
			peer->ppoll = peer->cfg.minpoll ;
		}
		if ( peer->cfg.maxpoll < peer->cfg.minpoll ) {
			peer->cfg.maxpoll = peer->cfg.minpoll ;
		}
		snprintf( sLog, sizeof(sLog), "minpoll=%d -> %d", oldminpoll, peer->cfg.minpoll ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_JJY, sLog ) ;
	}

	return false ;

}

/**************************************************************************************************/

static int
jjy_receive_telephone ( struct recvbuf *rbufp )
{
#ifdef DEBUG
	static	const char	*sFunctionName = "jjy_receive_telephone" ;
#endif

	struct	peer         *peer;
	struct	refclockproc *pp ;
	struct	jjyunit      *up ;
	char	*pBuf ;
	size_t	iLen ;
	short	iPreviousModemState ;

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_TELJJY_PRINTF( sFunctionName ) ;

	if ( up->iClockState == TELJJY_STATE_IDLE
	  || up->iClockState == TELJJY_STATE_DAILOUT
	  || up->iClockState == TELJJY_STATE_BYE ) {

		iPreviousModemState = getModemState( up ) ;

		modem_receive ( rbufp ) ;

		if ( iPreviousModemState != up->iModemState ) {
			/* Modem state is changed just now. */
			if ( isModemStateDisconnect( up->iModemState ) ) {
				up->iClockEvent = TELJJY_EVENT_DISCONNECT ;
				teljjy_control ( peer, pp, up ) ;
			} else if ( isModemStateConnect( up->iModemState ) ) {
				up->iClockEvent = TELJJY_EVENT_CONNECT ;
				teljjy_control ( peer, pp, up ) ;
			}
		}

		return JJY_RECEIVE_WAIT ;

	}

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = (size_t)up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = (size_t)pp->lencode ;
	}

	up->iTeljjySilentTimer = 0 ;
	if      ( iLen == 7 && strncmp( pBuf, "Name ? ", 7 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_LOGIN  ; }
	else if ( iLen == 1 && strncmp( pBuf, ">"      , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_PROMPT ; }
	else if ( iLen >= 1 && strncmp( pBuf, "?"      , 1 ) == 0 ) { up->iClockEvent = TELJJY_EVENT_ERROR  ; }
	else                                                        { up->iClockEvent = TELJJY_EVENT_DATA   ; }

	teljjy_control ( peer, pp, up ) ;

	return JJY_RECEIVE_WAIT ;

}

/**************************************************************************************************/

static void
jjy_poll_telephone ( int unit, struct peer *peer )
{
#ifdef DEBUG
	static const char *sFunctionName = "jjy_poll_telephone" ;
#endif

	struct	refclockproc *pp ;
	struct	jjyunit      *up ;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_TELJJY_PRINTF( sFunctionName ) ;

	if ( up->iClockState == TELJJY_STATE_IDLE ) {
		up->iRawBufLen = 0 ;
		up->iLineBufLen = 0 ;
		up->iTextBufLen = 0 ;
	}

	up->iClockEvent = TELJJY_EVENT_START ;
	teljjy_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static void
jjy_timer_telephone ( int unit, struct peer *peer )
{
#ifdef DEBUG
	static const char *sFunctionName = "jjy_timer_telephone" ;
#endif

	struct	refclockproc *pp ;
	struct	jjyunit      *up ;
	short	iPreviousModemState ;

	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_TELJJY_PRINTF( sFunctionName ) ;

	if ( iTeljjySilentTimeout[up->iClockState] != 0 ) {
		up->iTeljjySilentTimer++ ;
		if ( iTeljjySilentTimeout[up->iClockState] <= up->iTeljjySilentTimer ) {
			up->iClockEvent = TELJJY_EVENT_SILENT ;
			teljjy_control ( peer, pp, up ) ;
		}
	}

	if ( iTeljjyStateTimeout[up->iClockState] != 0 ) {
		up->iTeljjyStateTimer++ ;
		if ( iTeljjyStateTimeout[up->iClockState] <= up->iTeljjyStateTimer ) {
			up->iClockEvent = TELJJY_EVENT_TIMEOUT ;
			teljjy_control ( peer, pp, up ) ;
		}
	}

	if ( isModemStateTimerOn( up ) ) {

		iPreviousModemState = getModemState( up ) ;

		modem_timer ( unit, peer ) ;

		if ( iPreviousModemState != up->iModemState ) {
			/* Modem state is changed just now. */
			if ( isModemStateDisconnect( up->iModemState ) ) {
				up->iClockEvent = TELJJY_EVENT_DISCONNECT ;
				teljjy_control ( peer, pp, up ) ;
			} else if ( isModemStateConnect( up->iModemState ) ) {
				up->iClockEvent = TELJJY_EVENT_CONNECT ;
				teljjy_control ( peer, pp, up ) ;
			}
		}

	}

}

/**************************************************************************************************/

static void
teljjy_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	int	i, rc ;
	short	iPostEvent = TELJJY_EVENT_NULL ;

	DEBUG_TELJJY_PRINTF( "teljjy_control" ) ;

	rc = (*pTeljjyHandler[up->iClockEvent][up->iClockState])( peer, pp, up ) ;

	if ( rc == TELJJY_CHANGE_CLOCK_STATE ) {
		iPostEvent = iTeljjyPostEvent[up->iClockEvent][up->iClockState] ;
		DPRINT(1, ( "refclock_jjy.c : teljjy_control : iClockState=%hd -> %hd  iPostEvent=%hd\n",
			    up->iClockState, iTeljjyNextState[up->iClockEvent][up->iClockState], iPostEvent )) ;
		up->iTeljjySilentTimer = 0 ;
		if ( up->iClockState != iTeljjyNextState[up->iClockEvent][up->iClockState] ) {
			/* Telephone JJY state is changing now */
			up->iTeljjyStateTimer = 0 ;
			up->bLineError = false ;
			up->iClockCommandSeq = 0 ;
			up->iTimestampCount = 0 ;
			up->iLoopbackCount = 0 ;
			for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
				up->bLoopbackTimeout[i] = false ;
			}
			if (iTeljjyNextState[up->iClockEvent][up->iClockState] == TELJJY_STATE_IDLE ) {
				/* Telephone JJY state is changing to IDLE just now */
				up->iProcessState = JJY_PROCESS_STATE_DONE ;
			}
		}
		up->iClockState = iTeljjyNextState[up->iClockEvent][up->iClockState] ;

	}

	if ( iPostEvent != TELJJY_EVENT_NULL ) {
		up->iClockEvent = iPostEvent ;
		teljjy_control ( peer, pp, up ) ;
	}

	up->iClockEvent = TELJJY_EVENT_NULL ;

}

/**************************************************************************************************/

static void
teljjy_setDelay ( struct peer *peer, struct jjyunit *up )
{

	char	sLog [ MAX_LOGTEXT ] ;
	int	milliSecond, microSecond ;

	clock_gettime(CLOCK_REALTIME,
		      &(up->delayTime[up->iLoopbackCount]));

	up->delayTime[up->iLoopbackCount].tv_sec  -= up->sendTime[up->iLoopbackCount].tv_sec ;
	up->delayTime[up->iLoopbackCount].tv_nsec -= up->sendTime[up->iLoopbackCount].tv_nsec ;
	if ( up->delayTime[up->iLoopbackCount].tv_nsec < 0 ) {
		up->delayTime[up->iLoopbackCount].tv_sec -- ;
		up->delayTime[up->iLoopbackCount].tv_nsec += 1000000000 ;
	}

	milliSecond  = up->delayTime[up->iLoopbackCount].tv_nsec / 1000000 ;
	microSecond  = (up->delayTime[up->iLoopbackCount].tv_nsec/1000) - milliSecond * 1000 ;
	milliSecond += up->delayTime[up->iLoopbackCount].tv_sec * 1000 ;

	snprintf( sLog, sizeof(sLog), JJY_CLOCKSTATS_MESSAGE_LOOPBACK_DELAY,
		  milliSecond, microSecond ) ;

	if ( milliSecond > TELJJY_LOOPBACK_DELAY_THRESHOLD ) {
		/* Delay > 700 mS */
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;
	} else {
		/* Delay <= 700 mS */
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
	}

}

/**************************************************************************************************/

static int
teljjy_getDelay ( struct peer *peer, struct jjyunit *up )
{

	struct	timespec maxTime, minTime, averTime ;
	int	i ;
	int	minIndex = 0, maxIndex = 0, iAverCount = 0 ;
	int	iThresholdSecond, iThresholdMicroSecond ;
	int	iPercent ;

	minTime.tv_sec = minTime.tv_nsec = 0 ;
	maxTime.tv_sec = maxTime.tv_nsec = 0 ;

	iThresholdSecond = TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ;
	iThresholdMicroSecond = ( TELJJY_LOOPBACK_DELAY_THRESHOLD - ( TELJJY_LOOPBACK_DELAY_THRESHOLD / 1000 ) * 1000 ) * 1000 ;

	up->iLoopbackValidCount = 0 ;

	for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) {
		if ( up->bLoopbackTimeout[i]
		  || up->delayTime[i].tv_sec  > iThresholdSecond
		|| ( up->delayTime[i].tv_sec == iThresholdSecond
		  && up->delayTime[i].tv_nsec/1000 > iThresholdMicroSecond ) ) {
			continue ;
		}
		if ( up->iLoopbackValidCount == 0 ) {
			minTime.tv_sec  = up->delayTime[i].tv_sec  ;
			minTime.tv_nsec = up->delayTime[i].tv_nsec ;
			maxTime.tv_sec  = up->delayTime[i].tv_sec  ;
			maxTime.tv_nsec = up->delayTime[i].tv_nsec ;
			minIndex = maxIndex = i ;
		} else if ( minTime.tv_sec  > up->delayTime[i].tv_sec
		       || ( minTime.tv_sec == up->delayTime[i].tv_sec
		         && minTime.tv_nsec > up->delayTime[i].tv_nsec ) ) {
			minTime.tv_sec  = up->delayTime[i].tv_sec  ;
			minTime.tv_nsec = up->delayTime[i].tv_nsec ;
			minIndex = i ;
		} else if ( maxTime.tv_sec  < up->delayTime[i].tv_sec
		       || ( maxTime.tv_sec == up->delayTime[i].tv_sec
		         && maxTime.tv_nsec < up->delayTime[i].tv_nsec ) ) {
			maxTime.tv_sec  = up->delayTime[i].tv_sec  ;
			maxTime.tv_nsec = up->delayTime[i].tv_nsec ;
			maxIndex = i ;
		}
		up->iLoopbackValidCount ++ ;
	}

	if ( up->iLoopbackValidCount < 2 ) {
		return -1 ;
	}

	averTime.tv_nsec = 0;

	for ( i = 0 ; i < MAX_LOOPBACK && i < up->iLoopbackCount ; i ++ ) {
		if ( up->bLoopbackTimeout[i]
		  || up->delayTime[i].tv_sec  > iThresholdSecond
		|| ( up->delayTime[i].tv_sec == iThresholdSecond
		  && up->delayTime[i].tv_nsec/1000 > iThresholdMicroSecond ) ) {
			continue ;
		}
		if ( up->iLoopbackValidCount >= 3 && i == maxIndex ) {
			continue ;
		}
		if ( up->iLoopbackValidCount >= 4 && i == minIndex ) {
			continue ;
		}
		averTime.tv_nsec += up->delayTime[i].tv_nsec ;
		iAverCount ++ ;
	}

	if ( iAverCount == 0 ) {
		/* This is never happened. */
		/* Previous for-if-for blocks assure iAverCount > 0. */
		/* This code avoids a claim by the coverity scan tool. */
		return -1 ;
	}

	/* subtype 101 = 1%, subtype 150 = 50%, subtype 180 = 80% */

	iPercent = (int)peer->cfg.mode - 100 ;

	/* Average delay time in milli second */

	return ( ( (averTime.tv_nsec / 1000) / iAverCount ) * iPercent ) / 100000 ;

}

/******************************/
static int
teljjy_idle_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_idle_ignore" ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_idle_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_idle_dialout" ) ;

	modem_connect ( peer->procptr->refclkunit, peer ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_dial_ignore" ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_dial_login" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_dial_disc" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_login_ignore" ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_login_disc" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_conn ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	int	i ;

	UNUSED_ARG(peer);
	UNUSED_ARG(pp);

	DEBUG_TELJJY_PRINTF( "teljjy_login_conn" ) ;

	up->bLineError = false ;
	up->iClockCommandSeq = 0 ;
	up->iTimestampCount = 0 ;
	up->iLoopbackCount = 0 ;
	for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
		up->bLoopbackTimeout[i] = false ;
	}

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_login ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	const char	*pCmd ;
	int	iCmdLen ;

	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_login_login" ) ;

	/* Send a guest user ID */
	pCmd = "TJJY\r" ;

	/* Send login ID */
	iCmdLen = (int)strlen( pCmd ) ;
	if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	DEBUG_TELJJY_PRINTF( "teljjy_login_silent" ) ;

	if ( write( pp->io.fd, "\r", 1 ) != 1 ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, "\r" ) ;

	up->iTeljjySilentTimer = 0 ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_login_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_login_error" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_conn_ignore" ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_conn_disc" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_send ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	const char	*pCmd ;
	int	i, iLen, iNextClockState ;
	char	sLog[120];

	DEBUG_TELJJY_PRINTF( "teljjy_conn_send" ) ;

	if ( up->iClockCommandSeq > 0
	  && teljjy_command_sequence[up->iClockCommandSeq].command == NULL ) {
		/* Command sequence has been completed */
	  	return TELJJY_CHANGE_CLOCK_STATE ;
	}

	if ( up->iClockCommandSeq == 0 && peer->cfg.mode == 100 ) {
		/* Skip loopback */

		up->iClockCommandSeq = TELJJY_COMMAND_START_SKIP_LOOPBACK ;

	} else if ( up->iClockCommandSeq == 0 && peer->cfg.mode != 100 ) {
		/* Loopback start */

		up->iLoopbackCount = 0 ;
		for ( i = 0 ; i < MAX_LOOPBACK ; i ++ ) {
			up->bLoopbackTimeout[i] = false ;
		}

	} else if ( up->iClockCommandSeq > 0 && peer->cfg.mode != 100
		 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK
		 && up->iLoopbackCount < MAX_LOOPBACK ) {
		/* Loopback character comes */
	        DPRINT(1, ( "refclock_jjy.c : teljjy_conn_send : iClockCommandSeq=%d iLoopbackCount=%d\n",
			    up->iClockCommandSeq, up->iLoopbackCount )) ;

		teljjy_setDelay( peer, up ) ;

		up->iLoopbackCount ++ ;

	}

	up->iClockCommandSeq++ ;

	pCmd = teljjy_command_sequence[up->iClockCommandSeq].command ;
	iLen = teljjy_command_sequence[up->iClockCommandSeq].commandLength ;

	if ( pCmd != NULL ) {

		if ( write( pp->io.fd, pCmd, (size_t)iLen ) != iLen ) {
			refclock_report( peer, CEVNT_FAULT ) ;
		}

		if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
			/* Loopback character and timestamp */
			if ( up->iLoopbackCount < MAX_LOOPBACK ) {
				clock_gettime(CLOCK_REALTIME,
				      &(up->sendTime[up->iLoopbackCount]));
				up->bLoopbackMode = true ;
			} else {
				/* This else-block is never come. */
				/* This code avoid wrong report of the coverity static analysis scan tool. */
				snprintf( sLog, sizeof(sLog)-1, "refclock_jjy.c ; teljjy_conn_send ; iClockCommandSeq=%d iLoopbackCount=%d MAX_LOOPBACK=%d",
					  up->iClockCommandSeq, up->iLoopbackCount, MAX_LOOPBACK ) ;
				jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_BUG, sLog ) ;
				//msyslog ( LOG_ERR, sLog ) ;
				up->bLoopbackMode = false ;
			}
		} else {
			/* Regular command */
			up->bLoopbackMode = false ;
		}

		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

		if ( teljjy_command_sequence[up->iClockCommandSeq+1].command == NULL ) {
			/* Last command of the command sequence */
			iNextClockState = TELJJY_CHANGE_CLOCK_STATE ;
		} else {
			/* More commands to be issued */
			iNextClockState = TELJJY_STAY_CLOCK_STATE ;
		}

	} else {

		iNextClockState = TELJJY_CHANGE_CLOCK_STATE ;

	}

	return iNextClockState ;

}

/******************************/
static int
teljjy_conn_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	char	*pBuf ;
	int	iLen, rc ;
	char	sLog [ MAX_LOGTEXT ] ;
	char	bAdjustment ;


	DEBUG_TELJJY_PRINTF( "teljjy_conn_data" ) ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen
	  && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK
	  && up->sTextBuf[0] == *(teljjy_command_sequence[up->iClockCommandSeq].command)
	  && up->iLoopbackCount < MAX_LOOPBACK ) {
		/* Loopback */

		teljjy_setDelay( peer, up ) ;

		up->iLoopbackCount ++ ;

	} else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength == iLen
	    && strncmp( pBuf,
                teljjy_command_sequence[up->iClockCommandSeq].command,
                (size_t) iLen ) == 0 ) {
		/* Maybe echoback */

		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, JJY_CLOCKSTATS_MESSAGE_ECHOBACK ) ;

	} else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
		 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_4DATE ) {
		/* 4DATE<CR> -> YYYYMMDD<CR> */

		rc = sscanf ( pBuf, "%4d%2d%2d", &up->year, &up->month, &up->day ) ;

		if ( rc != 3 || up->year < 2000 || 2099 <= up->year
		  || up->month < 1 || 12 < up->month || up->day < 1 || 31 < up->day ) {
			/* Invalid date */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_DATE,
				  rc, up->year, up->month, up->day ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
		}

	} else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
		 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LEAPSEC
	         && ( strncmp( pBuf, " 0", 2 ) == 0 || strncmp( pBuf, "+1", 2 ) == 0 || strncmp( pBuf, "-1", 2 ) == 0 ) ) {
		/* LEAPSEC<CR> -> XX<CR> ( One of <SP>0, +1, -1 ) */

		rc = sscanf ( pBuf, "%2d", &up->leapsecond ) ;

		if ( rc != 1 || up->leapsecond < -1 || 1 < up->leapsecond ) {
			/* Invalid leap second */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_LEAP,
				  pBuf ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
		}

	} else if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyLength == iLen
		 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_TIME ) {
		/* TIME<CR> -> HHMMSS<CR> ( 3 times on second ) */

		rc = sscanf ( pBuf, "%2d%2d%2d", &up->hour, &up->minute, &up->second ) ;

		if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
			/* Invalid time */
			snprintf( sLog, sizeof(sLog),
                                  JJY_CLOCKSTATS_MESSAGE_SSCANF_INVALID_TIME,
				  rc, up->hour, up->minute, up->second ) ;
			jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
			up->bLineError = true ;
		}
		up->iTimestamp[up->iTimestampCount] = ( up->hour * 60 + up->minute ) * 60 + up->second ;

		up->iTimestampCount++ ;

		if ( up->iTimestampCount == 6 && ! up->bLineError ) {
#ifdef DEBUG
			printf( "refclock_jjy.c : teljjy_conn_data : bLineError=%d iTimestamp=%d, %d, %d\n",
				up->bLineError,
				up->iTimestamp[3], up->iTimestamp[4], up->iTimestamp[5] ) ;
#endif
			bAdjustment = true ;

			if ( peer->cfg.mode == 100 ) {
				/* subtype=100 */
				up->msecond = 0 ;
			} else {
				/* subtype=101 to 110 */
				up->msecond = teljjy_getDelay( peer, up ) ;
				if (up->msecond < 0 ) {
					up->msecond = 0 ;
					bAdjustment = false ;
				}
			}

			if ( ( up->iTimestamp[3] - 15 ) <= up->iTimestamp[2]
			  &&   up->iTimestamp[2]        <= up->iTimestamp[3]
			  && ( up->iTimestamp[3] +  1 ) == up->iTimestamp[4]
			  && ( up->iTimestamp[4] +  1 ) == up->iTimestamp[5] ) {
				/* Non over midnight */

				jjy_synctime( peer, pp, up ) ;

				if ( peer->cfg.mode != 100 ) {
					if ( bAdjustment ) {
						snprintf( sLog, sizeof(sLog),
							  JJY_CLOCKSTATS_MESSAGE_DELAY_ADJUST,
							  up->msecond, up->iLoopbackValidCount, MAX_LOOPBACK ) ;
						jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_INFORMATION, sLog ) ;
					} else {
						snprintf( sLog, sizeof(sLog),
							  JJY_CLOCKSTATS_MESSAGE_DELAY_UNADJUST,
							   up->iLoopbackValidCount, MAX_LOOPBACK ) ;
						jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;
					}
				}

			}
		}

	} else if ( teljjy_command_sequence[up->iClockCommandSeq].iEchobackReplyLength != iLen
		 && teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
		/* Loopback noise ( Unexpected replay ) */

		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_IGNORE_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_WARNING, sLog ) ;

	} else {

		up->bLineError = true ;

		snprintf( sLog, sizeof(sLog),
                          JJY_CLOCKSTATS_MESSAGE_UNEXPECTED_REPLY,
			  pBuf ) ;
		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_ERROR, sLog ) ;

	}

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	const char	*pCmd ;

	DEBUG_TELJJY_PRINTF( "teljjy_conn_silent" ) ;

	if ( up->iClockCommandSeq >= 1
	  && up->iClockCommandSeq < TELJJY_COMMAND_START_SKIP_LOOPBACK ) {
		/* Loopback */
		DPRINT(1, ( "refclock_jjy.c : teljjy_conn_silent : call teljjy_conn_send\n" )) ;
		if ( teljjy_command_sequence[up->iClockCommandSeq].iExpectedReplyType == TELJJY_REPLY_LOOPBACK ) {
			up->bLoopbackTimeout[up->iLoopbackCount] = true ;
		}
		up->iTeljjySilentTimer = 0 ;
		return teljjy_conn_send( peer, pp, up ) ;
	} else {
		pCmd = "\r" ;
	}

	if ( write( pp->io.fd, pCmd, 1 ) != 1 ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	up->iTeljjySilentTimer = 0 ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_conn_error ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_conn_error" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_bye_ignore" ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_bye_disc" ) ;

	return TELJJY_CHANGE_CLOCK_STATE ;

}

/******************************/
static int
teljjy_bye_modem ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_TELJJY_PRINTF( "teljjy_bye_modem" ) ;

	modem_disconnect ( peer->procptr->refclkunit, peer ) ;

	return TELJJY_STAY_CLOCK_STATE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    Modem control finite state machine							##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/

/* struct jjyunit.iModemState */

#define	MODEM_STATE_DISCONNECT		0
#define	MODEM_STATE_INITIALIZE		1
#define	MODEM_STATE_DAILING		2
#define	MODEM_STATE_CONNECT		3
#define	MODEM_STATE_ESCAPE		4

/* struct jjyunit.iModemEvent */

#define MODEM_EVENT_NULL		0
#define	MODEM_EVENT_INITIALIZE		1
#define	MODEM_EVENT_DIALOUT		2
#define	MODEM_EVENT_DISCONNECT		3
#define	MODEM_EVENT_RESP_OK		4
#define	MODEM_EVENT_RESP_CONNECT	5
#define	MODEM_EVENT_RESP_RING		6
#define	MODEM_EVENT_RESP_NO_CARRIER	7
#define	MODEM_EVENT_RESP_ERROR		8
#define	MODEM_EVENT_RESP_CONNECT_X	9
#define	MODEM_EVENT_RESP_NO_DAILTONE	10
#define	MODEM_EVENT_RESP_BUSY		11
#define	MODEM_EVENT_RESP_NO_ANSWER	12
#define	MODEM_EVENT_RESP_UNKNOWN	13
#define	MODEM_EVENT_SILENT		14
#define	MODEM_EVENT_TIMEOUT		15

/* Function prototypes */

static	void	modem_control		( struct peer *, struct refclockproc *, struct jjyunit * ) ;

static	int 	modem_disc_ignore	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_disc_init  	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_init_ignore	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_init_start 	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_init_disc  	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_init_resp00	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_init_resp04	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_dial_ignore	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_dial_dialout	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_dial_escape	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_dial_connect	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_dial_disc  	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_conn_ignore	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_conn_escape	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_esc_ignore	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_esc_escape	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_esc_data  	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_esc_silent	( struct peer *, struct refclockproc *, struct jjyunit * ) ;
static	int 	modem_esc_disc  	( struct peer *, struct refclockproc *, struct jjyunit * ) ;

static int ( *pModemHandler [ ] [ 5 ] ) (struct peer *, struct refclockproc *, struct jjyunit * ) =
{                         	/*STATE_DISCONNECT   STATE_INITIALIZE   STATE_DAILING       STATE_CONNECT      STATE_ESCAPE     */
/* NULL                 */	{ modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_ignore },
/* INITIALIZE           */	{ modem_disc_init  , modem_init_start , modem_dial_ignore , modem_conn_ignore, modem_esc_ignore },
/* DIALOUT              */	{ modem_disc_ignore, modem_init_ignore, modem_dial_dialout, modem_conn_ignore, modem_esc_ignore },
/* DISCONNECT           */	{ modem_disc_ignore, modem_init_disc  , modem_dial_escape , modem_conn_escape, modem_esc_escape },
/* RESP: 0: OK          */	{ modem_disc_ignore, modem_init_resp00, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* RESP: 1: CONNECT     */	{ modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data   },
/* RESP: 2: RING        */	{ modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* RESP: 3: NO CARRIER  */	{ modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 4: ERROR       */	{ modem_disc_ignore, modem_init_resp04, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 5: CONNECT     */	{ modem_disc_ignore, modem_init_ignore, modem_dial_connect, modem_conn_ignore, modem_esc_data   },
/* RESP: 6: NO DAILTONE */	{ modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 7: BUSY        */	{ modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 8: NO ANSWER   */	{ modem_disc_ignore, modem_init_ignore, modem_dial_disc   , modem_conn_ignore, modem_esc_data   },
/* RESP: 9: UNKNOWN     */	{ modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_data   },
/* SILENT               */	{ modem_disc_ignore, modem_init_ignore, modem_dial_ignore , modem_conn_ignore, modem_esc_silent },
/* TIMEOUT              */	{ modem_disc_ignore, modem_init_disc  , modem_dial_escape , modem_conn_escape, modem_esc_disc   }
} ;

static short iModemNextState [ ] [ 5 ] =
{                         	/*STATE_DISCONNECT        STATE_INITIALIZE        STATE_DAILING        STATE_CONNECT        STATE_ESCAPE           */
/* NULL                 */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* INITIALIZE           */	{ MODEM_STATE_INITIALIZE, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* DIALOUT              */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* DISCONNECT           */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE    , MODEM_STATE_ESCAPE , MODEM_STATE_ESCAPE     },
/* RESP: 0: OK          */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING   , MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 1: CONNECT     */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 2: RING        */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 3: NO CARRIER  */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 4: ERROR       */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_DAILING   , MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 5: CONNECT X   */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_CONNECT   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 6: NO DAILTONE */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 7: BUSY        */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 8: NO ANSWER   */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DISCONNECT, MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* RESP: 9: UNKNOWN     */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_ESCAPE     },
/* SILENT               */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_INITIALIZE, MODEM_STATE_DAILING   , MODEM_STATE_CONNECT, MODEM_STATE_DISCONNECT },
/* TIMEOUT              */	{ MODEM_STATE_DISCONNECT, MODEM_STATE_DISCONNECT, MODEM_STATE_ESCAPE    , MODEM_STATE_ESCAPE , MODEM_STATE_DISCONNECT }
} ;

static short iModemPostEvent [ ] [ 5 ] =
{                         	/*STATE_DISCONNECT        STATE_INITIALIZE     STATE_DAILING           STATE_CONNECT           STATE_ESCAPE     */
/* NULL                 */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* INITIALIZE           */	{ MODEM_EVENT_INITIALIZE, MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* DIALOUT              */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* DISCONNECT           */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL },
/* RESP: 0: OK          */	{ MODEM_EVENT_NULL      , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 1: CONNECT     */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 2: RING        */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 3: NO CARRIER  */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 4: ERROR       */	{ MODEM_EVENT_NULL      , MODEM_EVENT_DIALOUT, MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 5: CONNECT X   */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 6: NO DAILTONE */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 7: BUSY        */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 8: NO ANSWER   */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* RESP: 9: UNKNOWN     */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* SILENT               */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_NULL      , MODEM_EVENT_NULL      , MODEM_EVENT_NULL },
/* TIMEOUT              */	{ MODEM_EVENT_NULL      , MODEM_EVENT_NULL   , MODEM_EVENT_DISCONNECT, MODEM_EVENT_DISCONNECT, MODEM_EVENT_NULL }
} ;

static short iModemSilentTimeout [ 5 ] = { 0,  0,  0, 0,  5 } ;
static short iModemStateTimeout  [ 5 ] = { 0, 20, 90, 0, 20 } ;

#define	STAY_MODEM_STATE	0
#define	CHANGE_MODEM_STATE	1

#ifdef	DEBUG
#define	DEBUG_MODEM_PRINTF(sFunc)	{ if ( debug ) { printf ( "refclock_jjy.c : %s : iModemState=%d iModemEvent=%d iModemSilentTimer=%d iModemStateTimer=%d\n", sFunc, up->iModemState, up->iModemEvent, up->iModemSilentTimer, up->iModemStateTimer ) ; } }
#else
#define	DEBUG_MODEM_PRINTF(sFunc)
#endif

/**************************************************************************************************/

static short
getModemState ( struct jjyunit *up )
{
	return up->iModemState ;
}

/**************************************************************************************************/

static int
isModemStateConnect ( short iCheckState )
{
	return ( iCheckState == MODEM_STATE_CONNECT ) ;
}

/**************************************************************************************************/

static int
isModemStateDisconnect ( short iCheckState )
{
	return ( iCheckState == MODEM_STATE_DISCONNECT ) ;
}

/**************************************************************************************************/

static int
isModemStateTimerOn ( struct jjyunit *up )
{
	return ( iModemSilentTimeout[up->iModemState] != 0 || iModemStateTimeout[up->iModemState] != 0 ) ;
}

/**************************************************************************************************/

static void
modem_connect ( int unit, struct peer *peer )
{
	struct	refclockproc	*pp;
	struct	jjyunit 	*up;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_MODEM_PRINTF( "modem_connect" ) ;

	up->iModemEvent = MODEM_EVENT_INITIALIZE ;

	modem_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static void
modem_disconnect ( int unit, struct peer *peer )
{
	struct	refclockproc	*pp;
	struct	jjyunit 	*up;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_MODEM_PRINTF( "modem_disconnect" ) ;

	up->iModemEvent = MODEM_EVENT_DISCONNECT ;

	modem_control ( peer, pp, up ) ;

}

/**************************************************************************************************/

static int
modem_receive ( struct recvbuf *rbufp )
{

	struct	peer		*peer;
	struct	jjyunit		*up;
	struct	refclockproc	*pp;
	char	*pBuf ;
	int	iLen ;

#ifdef DEBUG
	static const char *sFunctionName = "modem_receive" ;
#endif

	peer = rbufp->recv_peer ;
	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_MODEM_PRINTF( sFunctionName ) ;

	if ( up->linediscipline == LDISC_RAW ) {
		pBuf = up->sTextBuf ;
		iLen = up->iTextBufLen ;
	} else {
		pBuf = pp->a_lastcode ;
		iLen = pp->lencode ;
	}

	if      ( iLen ==  2 && strncmp( pBuf, "OK"         ,  2 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_OK          ; }
	else if ( iLen ==  7 && strncmp( pBuf, "CONNECT"    ,  7 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT     ; }
	else if ( iLen ==  4 && strncmp( pBuf, "RING"       ,  4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_RING        ; }
	else if ( iLen == 10 && strncmp( pBuf, "NO CARRIER" , 10 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_CARRIER  ; }
	else if ( iLen ==  5 && strncmp( pBuf, "ERROR"      ,  5 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_ERROR       ; }
	else if ( iLen >=  8 && strncmp( pBuf, "CONNECT "   ,  8 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_CONNECT_X   ; }
	else if ( iLen == 11 && strncmp( pBuf, "NO DAILTONE", 11 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_DAILTONE ; }
	else if ( iLen ==  4 && strncmp( pBuf, "BUSY"       ,  4 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_BUSY        ; }
	else if ( iLen ==  9 && strncmp( pBuf, "NO ANSWER"  ,  9 ) == 0 ) { up->iModemEvent = MODEM_EVENT_RESP_NO_ANSWER   ; }
	else                                                              { up->iModemEvent = MODEM_EVENT_RESP_UNKNOWN     ; }

#ifdef DEBUG
	if ( debug ) { /* SPECIAL DEBUG */
		char	sResp [ 40 ] ;
		size_t	iCopyLen ;
		iCopyLen = iLen <= (int)sizeof(sResp)-1 ?
                               (size_t)iLen : sizeof(sResp) - 1U;
		strlcpy( sResp, pBuf, sizeof(sResp) ) ;
		printf( "refclock_jjy.c : modem_receive : iLen=%zu "
                        "pBuf=[%s] iModemEvent=%d\n",
                        iCopyLen, sResp, up->iModemEvent ) ;
	}
#endif
	modem_control ( peer, pp, up ) ;

	return 0 ;

}

/**************************************************************************************************/

static void
modem_timer ( int unit, struct peer *peer )
{

	struct	refclockproc *pp ;
	struct	jjyunit      *up ;

	UNUSED_ARG(unit);

	pp = peer->procptr ;
	up = pp->unitptr ;

	DEBUG_MODEM_PRINTF( "modem_timer" ) ;

	if ( iModemSilentTimeout[up->iModemState] != 0 ) {
		up->iModemSilentTimer++ ;
		if ( iModemSilentTimeout[up->iModemState] <= up->iModemSilentTimer ) {
			up->iModemEvent = MODEM_EVENT_SILENT ;
			modem_control ( peer, pp, up ) ;
		}
	}

	if ( iModemStateTimeout[up->iModemState] != 0 ) {
		up->iModemStateTimer++ ;
		if ( iModemStateTimeout[up->iModemState] <= up->iModemStateTimer ) {
			up->iModemEvent = MODEM_EVENT_TIMEOUT ;
			modem_control ( peer, pp, up ) ;
		}
	}

}

/**************************************************************************************************/

static void
modem_control ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	int	rc ;
	short	iPostEvent = MODEM_EVENT_NULL ;

	DEBUG_MODEM_PRINTF( "modem_control" ) ;

	rc = (*pModemHandler[up->iModemEvent][up->iModemState])( peer, pp, up ) ;

	if ( rc == CHANGE_MODEM_STATE ) {
		iPostEvent = iModemPostEvent[up->iModemEvent][up->iModemState] ;
		DPRINT(1, ( "refclock_jjy.c : modem_control : iModemState=%d -> %d  iPostEvent=%d\n",
			    up->iModemState, iModemNextState[up->iModemEvent][up->iModemState], iPostEvent )) ;

		if ( up->iModemState != iModemNextState[up->iModemEvent][up->iModemState] ) {
			up->iModemSilentCount = 0 ;
			up->iModemStateTimer = 0 ;
			up->iModemCommandSeq = 0 ;
		}

		up->iModemState = iModemNextState[up->iModemEvent][up->iModemState] ;
	}

	if ( iPostEvent != MODEM_EVENT_NULL ) {
		up->iModemEvent = iPostEvent ;
		modem_control ( peer, pp, up ) ;
	}

	up->iModemEvent = MODEM_EVENT_NULL ;

}

/******************************/
static int
modem_disc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_disc_ignore" ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_disc_init ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_disc_init" ) ;

	return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_init_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_init_ignore" ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_init_start ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);

	DEBUG_MODEM_PRINTF( "modem_init_start" ) ;

	up->iModemCommandSeq = 0 ;

	DPRINT(1, ( "refclock_jjy.c : modem_init_start : call modem_init_resp00\n" )) ;

	return modem_init_resp00( peer, pp, up ) ;

}

/******************************/
static int
modem_init_resp00 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	const char *pCmd;
	char	cBuf [ 46 ] ;
	int	iCmdLen ;
	int	iErrorCorrection, iSpeakerSwitch, iSpeakerVolume ;
	int	iNextModemState = STAY_MODEM_STATE ;

	DEBUG_MODEM_PRINTF( "modem_init_resp00" ) ;

	up->iModemCommandSeq++ ;

	switch ( up->iModemCommandSeq ) {

	case 1 :
		/* En = Echoback      0:Off      1:On   */
		/* Qn = Result codes  0:On       1:Off  */
		/* Vn = Result codes  0:Numeric  1:Text */
		pCmd = "ATE0Q0V1\r\n" ;
		break ;

	case 2 :
		/* Mn = Speaker switch  0:Off  1:On until remote carrier detected  2:On */
		if ( ( pp->sloppyclockflag & CLK_FLAG3 ) == 0 ) {
			/* refclock jjy unit n flag3 0 */
			iSpeakerSwitch = 0 ;
		} else {
			/* refclock jjy unit n flag3 1 */
			iSpeakerSwitch = 2 ;
		}

		/* Ln = Speaker volume  0:Very low  1:Low  2:Middle  3:High */
		if ( ( pp->sloppyclockflag & CLK_FLAG4 ) == 0 ) {
			/* refclock jjy unit n flag4 0 */
			iSpeakerVolume = 1 ;
		} else {
			/* refclock jjy unit n flag4 1 */
			iSpeakerVolume = 2 ;
		}

		snprintf(cBuf, sizeof(cBuf), "ATM%dL%d\r\n",
                         iSpeakerSwitch, iSpeakerVolume ) ;
		pCmd = cBuf ;
		break ;

	case 3 :
		/* &Kn = Flow control  4:XON/XOFF */
		pCmd = "AT&K4\r\n" ;
		break ;

	case 4 :
		/* +MS = Protocol  V22B:1200,2400 bps V.22bis) */
		pCmd = "AT+MS=V22B\r\n" ;
		break ;

	case 5 :
		/* %Cn = Data compression  0:No data compression */
		pCmd = "AT%C0\r\n" ;
		break ;

	case 6 :
		/* \Nn = Error correction  0:Normal mode  1:Direct mode  2:V42,MNP  3:V42,MNP,Normal */
		if ( ( pp->sloppyclockflag & CLK_FLAG2 ) == 0 ) {
			/* refclock jjy unit n flag2 0 */
			iErrorCorrection = 0 ;
		} else {
			/* refclock jjy unit n flag2 1 */
			iErrorCorrection = 3 ;
		}

		snprintf(cBuf, sizeof(cBuf), "AT\\N%d\r\n", iErrorCorrection);
		pCmd = cBuf ;
		break ;

	case 7 :
		/* Hn = Hook  0:Hook-On ( Disconnect )  1:Hook-Off ( Connect ) */
		pCmd = "ATH1\r\n" ;
		break ;

	case 8 :
		/* Initialize completion */
		pCmd = NULL ;
		iNextModemState = CHANGE_MODEM_STATE ;
		break ;

	default :
		pCmd = NULL ;
		break ;

	}

	if ( pCmd != NULL ) {

		iCmdLen = (int)strlen( pCmd ) ;
		if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
			refclock_report( peer, CEVNT_FAULT ) ;
		}

		jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	}

	return iNextModemState ;

}

/******************************/
static int
modem_init_resp04 ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	DEBUG_MODEM_PRINTF( "modem_init_resp04" ) ;

	return modem_init_resp00( peer, pp, up ) ;

}

/******************************/
static int
modem_init_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_init_disc" ) ;
	DPRINT(1, ( "refclock_jjy.c : modem_init_disc : call modem_esc_disc\n" )) ;

	return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_dial_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_dial_ignore" ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_dial_dialout ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(up);

	char	sCmd [ 46 ] ;
	int	iCmdLen ;
	char	cToneOrPulse ;

	DEBUG_MODEM_PRINTF( "modem_dial_dialout" ) ;

	/* Tone or Pulse */
	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
		/* refclock jjy unit n flag1 0 */
		cToneOrPulse = 'T' ;
	} else {
		/* refclock jjy unit n flag1 1 */
		cToneOrPulse = 'P' ;
	}

	/* Connect ( Dial number ) */
	snprintf( sCmd, sizeof(sCmd), "ATDW%c%s\r\n", cToneOrPulse, *sys_phone ) ;

	/* Send command */
	iCmdLen = (int)strlen( sCmd ) ;
	if ( write( pp->io.fd, sCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, sCmd ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_dial_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	DEBUG_MODEM_PRINTF( "modem_dial_escape" ) ;
	DPRINT(1, ( "refclock_jjy.c : modem_dial_escape : call modem_conn_escape\n" )) ;

	return modem_conn_escape( peer, pp, up ) ;

}

/******************************/
static int
modem_dial_connect ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_dial_connect" ) ;

	return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_dial_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	DEBUG_MODEM_PRINTF( "modem_dial_disc" ) ;
	DPRINT(1, ( "refclock_jjy.c : modem_dial_disc : call modem_esc_disc\n" )) ;

	modem_esc_disc( peer, pp, up ) ;

	return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_conn_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_conn_ignore" ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_conn_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_conn_escape" ) ;

	return CHANGE_MODEM_STATE ;

}

/******************************/
static int
modem_esc_ignore ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);
	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_esc_ignore" ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_escape ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(up);

	const char	*pCmd ;
	int	iCmdLen ;

	DEBUG_MODEM_PRINTF( "modem_esc_escape" ) ;

	/* Escape command ( Go to command mode ) */
	pCmd = "+++" ;

	/* Send command */
	iCmdLen = (int)strlen( pCmd ) ;
	if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_data ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	UNUSED_ARG(peer);
	UNUSED_ARG(pp);

	DEBUG_MODEM_PRINTF( "modem_esc_data" ) ;

	up->iModemSilentTimer = 0 ;

	return STAY_MODEM_STATE ;

}

/******************************/
static int
modem_esc_silent ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{
	DEBUG_MODEM_PRINTF( "modem_esc_silent" ) ;

	up->iModemSilentCount ++ ;

	if ( up->iModemSilentCount < iModemStateTimeout[up->iModemState] / iModemSilentTimeout[up->iModemState] ) {
		DPRINT(1, ( "refclock_jjy.c : modem_esc_silent : call modem_esc_escape\n" )) ;
		modem_esc_escape( peer, pp, up ) ;
		up->iModemSilentTimer = 0 ;
		return STAY_MODEM_STATE ;
	}

	DPRINT(1, ( "refclock_jjy.c : modem_esc_silent : call modem_esc_disc\n" )) ;
	return modem_esc_disc( peer, pp, up ) ;

}
/******************************/
static int
modem_esc_disc ( struct peer *peer, struct refclockproc *pp, struct jjyunit *up )
{

	const char	*pCmd ;
	int	iCmdLen ;

	UNUSED_ARG(up);

	DEBUG_MODEM_PRINTF( "modem_esc_disc" ) ;

	/* Disconnect */
	pCmd = "ATH0\r\n" ;

	/* Send command */
	iCmdLen = (int)strlen( pCmd ) ;
	if ( write( pp->io.fd, pCmd, (size_t)iCmdLen ) != iCmdLen ) {
		refclock_report( peer, CEVNT_FAULT ) ;
	}

	jjy_write_clockstats( peer, JJY_CLOCKSTATS_MARK_SEND, pCmd ) ;

	return CHANGE_MODEM_STATE ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    jjy_write_clockstats									##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/

static void
jjy_write_clockstats ( struct peer *peer, int iMark, const char *pData )
{

	char	sLog [ MAX_LOGTEXT ] ;
	const char *pMark ;
	int 	iMarkLen, iDataLen ;

	switch ( iMark ) {
	case JJY_CLOCKSTATS_MARK_JJY :
		pMark = "JJY " ;
		break ;
	case JJY_CLOCKSTATS_MARK_SEND :
		pMark = "--> " ;
		break ;
	case JJY_CLOCKSTATS_MARK_RECEIVE :
		pMark = "<-- " ;
		break ;
	case JJY_CLOCKSTATS_MARK_INFORMATION :
		pMark = "--- " ;
		break ;
	case JJY_CLOCKSTATS_MARK_ATTENTION :
		pMark = "=== " ;
		break ;
	case JJY_CLOCKSTATS_MARK_WARNING :
		pMark = "-W- " ;
		break ;
	case JJY_CLOCKSTATS_MARK_ERROR :
		pMark = "-X- " ;
		break ;
	case JJY_CLOCKSTATS_MARK_BUG :
		pMark = "!!! " ;
		break ;
	default :
		pMark = "" ;
		break ;
	}

	iDataLen = (int)strlen( pData ) ;
	iMarkLen = (int)strlen( pMark ) ;
	strlcpy( sLog, pMark, sizeof( sLog )) ;
	printableString( sLog+iMarkLen, (int)sizeof(sLog)-iMarkLen,
                         pData, iDataLen ) ;

	DPRINT(1, ( "refclock_jjy.c : clockstats : %s\n", sLog )) ;
	record_clock_stats( peer, sLog ) ;

}

/*################################################################################################*/
/*################################################################################################*/
/*##												##*/
/*##    printableString										##*/
/*##												##*/
/*################################################################################################*/
/*################################################################################################*/

static void
printableString ( char *sOutput, int iOutputLen, const char *sInput, int iInputLen )
{
	const char	*printableControlChar[] = {
			"<NUL>", "<SOH>", "<STX>", "<ETX>",
			"<EOT>", "<ENQ>", "<ACK>", "<BEL>",
			"<BS>" , "<HT>" , "<LF>" , "<VT>" ,
			"<FF>" , "<CR>" , "<SO>" , "<SI>" ,
			"<DLE>", "<DC1>", "<DC2>", "<DC3>",
			"<DC4>", "<NAK>", "<SYN>", "<ETB>",
			"<CAN>", "<EM>" , "<SUB>", "<ESC>",
			"<FS>" , "<GS>" , "<RS>" , "<US>" ,
			" " } ;

	size_t	i, j, n ;
	size_t	InputLen;
	size_t	OutputLen;

	InputLen = (size_t)iInputLen;
	OutputLen = (size_t)iOutputLen;
	for ( i = j = 0 ; i < InputLen && j < OutputLen ; i ++ ) {
		if ( isprint( (unsigned char)sInput[i] ) ) {
			n = 1 ;
			if ( j + 1 >= OutputLen ) {
				break ;
			}
			sOutput[j] = sInput[i] ;
		} else if ( ( sInput[i] & 0xFF ) <
			    COUNTOF(printableControlChar) ) {
			n = strlen( printableControlChar[sInput[i] & 0xFF] ) ;
			if ( j + n + 1 >= OutputLen ) {
				break ;
			}
			strlcpy( sOutput + j,
				 printableControlChar[sInput[i] & 0xFF],
				 OutputLen - j ) ;
		} else {
			n = 5 ;
			if ( j + n + 1 >= OutputLen ) {
				break ;
			}
			snprintf( sOutput + j, OutputLen - j, "<x%X>",
				  (unsigned)(sInput[i] & 0xFF) ) ;
		}
		j += n ;
	}

	sOutput[min(j, (size_t)iOutputLen - 1)] = '\0' ;

}

/**************************************************************************************************/

