/*
 * refclock_jjy - clock driver for JJY receivers
 */

/**********************************************************************/
/*								      */
/*  Copyright (C) 2001-2004, Takao Abe.  All rights reserved.	      */
/*								      */
/*  Permission to use, copy, modify, and distribute this software     */
/*  and its documentation for any purpose is hereby granted	      */
/*  without fee, provided that the following conditions are met:      */
/*								      */
/*  One retains the entire copyright notice properly, and both the    */
/*  copyright notice and this license. in the documentation and/or    */
/*  other materials provided with the distribution.		      */
/*								      */
/*  This software and the name of the author must not be used to      */
/*  endorse or promote products derived from this software without    */
/*  prior written permission.					      */
/*								      */
/*  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED    */
/*  WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE	      */
/*  IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A	      */
/*  PARTICULAR PURPOSE.						      */
/*  IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT,  */
/*  INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   */
/*  ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE	      */
/*  GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS      */
/*  INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,     */
/*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING	      */
/*  NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF    */
/*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
/*								      */
/*  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 "fudge 127.127.40.X flag1 1" is specified	      */
/*	       ( DATE,STIM -> DCST,STUS,DATE,STIM )		      */
/*								      */
/**********************************************************************/

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#if defined(REFCLOCK) && defined(CLOCK_JJY)

#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"

/**********************************************************************/
/*								      */
/*  The Tristate Ltd. JJY receiver JJY01			      */
/*								      */
/*  Command	   Response		    Remarks		      */
/*  ------------   ----------------------   ---------------------     */
/*  dcst<CR><LF>   VALID|INVALID<CR><LF>			      */
/*  stus<CR><LF>   ADJUSTED|UNADJUSTED<CR><LF>			      */
/*  date<CR><LF>   YYYY/MM/DD XXX<CR><LF>			      */
/*  time<CR><LF>   HH:MM:SS<CR><LF>				      */
/*  stim<CR><LF>   HH:MM:SS<CR><LF>	    Reply at just second      */
/*								      */
/*  During synchronization after a receiver is turned on,	      */
/*  It replies the past time from 2000/01/01 00:00:00.		      */
/*  The function "refclock_process" checks the time and tells	      */
/*  as an insanity time.					      */
/*								      */
/**********************************************************************/
/*								      */
/*  The C-DEX Co. Ltd. JJY receiver JST2000			      */
/*								      */
/*  Command	   Response		    Remarks		      */
/*  ------------   ----------------------   ---------------------     */
/*  <ENQ>1J<ETX>   <STX>JYYMMDD HHMMSSS<ETX>			      */
/*								      */
/**********************************************************************/
/*								      */
/*  The Echo Keisokuki Co. Ltd. JJY receiver LT2000		      */
/*								      */
/*  Command        Response		    Remarks		      */
/*  ------------   ----------------------   ---------------------     */
/*  #					    Mode 1 (Request&Send)     */
/*  T		   YYMMDDWHHMMSS<BCC1><BCC2><CR>		      */
/*  C					    Mode 2 (Continuous)	      */
/*		   YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR>	      */
/*		   <SUB>		    Second signal	      */
/*								      */
/**********************************************************************/
/*								      */
/*  The CITIZEN T.I.C CO., LTD. JJY receiver JJY200		      */
/*								      */
/*  Command	   Response		    Remarks		      */
/*  ------------   ----------------------   ---------------------     */
/*		   'XX YY/MM/DD W HH:MM:SS<CR>			      */
/*					    XX: OK|NG|ER	      */
/*					    W:  0(Monday)-6(Sunday)   */
/*								      */
/**********************************************************************/

/*
 * Interface definitions
 */
#define	DEVICE  	"/dev/jjy%d"	/* device name and unit */
#define	SPEED232	B9600		/* uart speed (9600 baud) */
#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	REFID   	"JJY"		/* reference ID */
#define	DESCRIPTION	"JJY Receiver"
#define	PRECISION	(-3)		/* precision assumed (about 100 ms) */

/*
 * JJY unit control structure
 */
struct jjyunit {
	char	unittype ;	    /* UNITTYPE_XXXXXXXXXX */
	short   operationmode ;	    /* Echo Keisokuki LT-2000 : 1 or 2 */
	short	version ;
	short	linediscipline ;    /* LDISC_CLK or LDISC_RAW */
	char    bPollFlag ;	    /* Set by jjy_pool and Reset by jjy_receive */
	int 	linecount ;
	int 	lineerror ;
	int 	year, month, day, hour, minute, second, msecond ;
/* LDISC_RAW only */
#define	MAX_LINECOUNT	8
#define	MAX_RAWBUF   	64
	int 	lineexpect ;
	int 	charexpect [ MAX_LINECOUNT ] ;
	int 	charcount ;
	char	rawbuf [ MAX_RAWBUF ] ;
};

#define	UNITTYPE_TRISTATE_JJY01	1
#define	UNITTYPE_CDEX_JST2000  	2
#define	UNITTYPE_ECHOKEISOKUKI_LT2000  	3
#define	UNITTYPE_CITIZENTIC_JJY200  	4

/*
 * Function prototypes
 */
static	int 	jjy_start		    (int, struct peer *);
static	void	jjy_shutdown		    (int, struct peer *);
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_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	void	printableString ( char*, int, char*, int ) ;

/*
 * Transfer vector
 */
struct	refclock refclock_jjy = {
	jjy_start,	/* start up driver */
	jjy_shutdown,	/* shutdown driver */
	jjy_poll,	/* transmit poll message */
	noentry,	/* not used */
	noentry,	/* not used */
	noentry,	/* not used */
	NOFLAGS		/* not used */
};

/*
 * Start up driver return code
 */
#define	RC_START_SUCCESS	1
#define	RC_START_ERROR		0

/*
 * Local constants definition
 */

#define	MAX_LOGTEXT	64

/*
 * Tristate JJY01/JJY02 constants definition
 */

#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\r\n"
#define	TS_JJY01_REPLY_STIM		"hh:mm:ss\r\n"
#define	TS_JJY01_REPLY_STUS_YES		"adjusted\r\n"
#define	TS_JJY01_REPLY_STUS_NO		"unadjusted\r\n"
#define	TS_JJY01_REPLY_DCST_VALID	"valid\r\n"
#define	TS_JJY01_REPLY_DCST_INVALID	"invalid\r\n"

#define	TS_JJY01_REPLY_LENGTH_DATE	    14	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STIM	    8	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STUS_YES	    8	/* Length without <CR><LF> */
#define	TS_JJY01_REPLY_LENGTH_STUS_NO	    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
{
	char	commandNumber ;
	char	*commandLog ;
	char	*command ;
	int	commandLength ;
} tristate_jjy01_command_sequence[] =
{
	/* dcst<CR><LF> -> VALID<CR><LF> or INVALID<CR><LF> */
	{ TS_JJY01_COMMAND_NUMBER_DCST, "dcst", "dcst\r\n", 6 },
	/* stus<CR><LF> -> ADJUSTED<CR><LF> or UNADJUSTED<CR><LF> */
	{ TS_JJY01_COMMAND_NUMBER_STUS, "stus", "stus\r\n", 6 },
	/* date<CR><LF> -> YYYY/MM/DD WWW<CR><LF> */
	{ TS_JJY01_COMMAND_NUMBER_DATE, "date", "date\r\n", 6 },
	/* stim<CR><LF> -> HH:MM:SS<CR><LF> */
	{ TS_JJY01_COMMAND_NUMBER_STIM, "stim", "stim\r\n", 6 },
	{ 0			      , NULL  , NULL	  , 0 }
} ;


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

	struct jjyunit	    *up ;
	struct refclockproc *pp ;
	int 	fd ;
	char	*pDeviceName ;
	short	iDiscipline ;
	int 	iSpeed232 ;

#ifdef DEBUG
	if ( debug ) {
		printf ( "jjy_start (refclock_jjy.c) : %s  mode=%d  ", ntoa(&peer->srcadr), peer->ttl ) ;
		printf ( DEVICE, unit ) ;
		printf ( "\n" ) ;
	}
#endif
	/*
	 * Open serial port
	 */
	pDeviceName = emalloc ( strlen(DEVICE) + 10 );
	snprintf ( pDeviceName, strlen(DEVICE) + 10, DEVICE, unit ) ;

	/*
	 * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
	 */
	switch ( peer->ttl ) {
	case 0 :
	case 1 :
		iDiscipline = LDISC_CLK ;
		iSpeed232   = SPEED232_TRISTATE_JJY01 ;
		break ;
	case 2 :
		iDiscipline = LDISC_RAW ;
		iSpeed232   = SPEED232_CDEX_JST2000   ;
		break ;
	case 3 :
		iDiscipline = LDISC_CLK ;
		iSpeed232   = SPEED232_ECHOKEISOKUKI_LT2000 ;
		break ;
	case 4 :
		iDiscipline = LDISC_CLK ;
		iSpeed232   = SPEED232_CITIZENTIC_JJY200 ;
		break ;
	default :
		msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
			  ntoa(&peer->srcadr), peer->ttl ) ;
		free ( (void*) pDeviceName ) ;
		return RC_START_ERROR ;
	}

	if ( ! ( fd = refclock_open ( pDeviceName, iSpeed232, iDiscipline ) ) ) {
		free ( (void*) pDeviceName ) ;
		return RC_START_ERROR ;
	}
	free ( (void*) pDeviceName ) ;

	/*
	 * Allocate and initialize unit structure
	 */
	up = emalloc (sizeof(*up));
	memset ( up, 0, sizeof(*up) ) ;
	up->linediscipline = iDiscipline ;

	/*
	 * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
	 */
	switch ( peer->ttl ) {
	case 0 :
		/*
		 * The mode 0 is a default clock type at this time.
		 * But this will be change to auto-detect mode in the future.
		 */
	case 1 :
		up->unittype = UNITTYPE_TRISTATE_JJY01 ;
		up->version  = 100 ;
		/* 2010/11/20 */
		/* Command sequence is defined by the struct tristate_jjy01_command_sequence, */
		/* and the following 3 lines are not used in the mode LDISC_CLK. */
		/* up->lineexpect = 2 ; */
		/* up->charexpect[0] = 14 ; */ /* YYYY/MM/DD WWW<CR><LF> */
		/* up->charexpect[1] =  8 ; */ /* HH:MM:SS<CR><LF> */
		break ;
	case 2 :
		up->unittype = UNITTYPE_CDEX_JST2000 ;
		up->lineexpect = 1 ;
		up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */
		break ;
	case 3 :
		up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
		up->operationmode = 2 ;  /* Mode 2 : Continuous mode */
		up->lineexpect = 1 ;
		switch ( up->operationmode ) {
		case 1 :
			up->charexpect[0] = 15 ; /* YYMMDDWHHMMSS<BCC1><BCC2><CR> */
			break ;
		case 2 :
			up->charexpect[0] = 17 ; /* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
			break ;
		}
		break ;
	case 4 :
		up->unittype = UNITTYPE_CITIZENTIC_JJY200 ;
		up->lineexpect = 1 ;
		up->charexpect[0] = 23 ; /* 'XX YY/MM/DD W HH:MM:SS<CR> */
		break ;

	/* 2010/11/20 */
	/* The "default:" section of this switch block is never executed,     */
	/* because the former switch block traps the same "default:" case.    */
	/* This "default:" section codes are removed to avoid spending time   */
	/* in the future looking, though the codes are functionally harmless. */

	}

	pp = peer->procptr ;
	pp->unitptr       = (caddr_t) up ;
	pp->io.clock_recv = jjy_receive ;
	pp->io.srcclock   = (caddr_t) 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 RC_START_ERROR ;
	}

	/*
	 * Initialize miscellaneous variables
	 */
	peer->precision = PRECISION ;
	peer->burst	= 1 ;
	pp->clockdesc	= DESCRIPTION ;
	memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ;

	return RC_START_SUCCESS ;

}


/**************************************************************************************************/
/*  jjy_shutdown - shutdown the clock                                                             */
/**************************************************************************************************/
static void
jjy_shutdown ( int unit, struct peer *peer )
{

	struct jjyunit	    *up;
	struct refclockproc *pp;

	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;
	if ( -1 != pp->io.fd )
		io_closeclock ( &pp->io ) ;
	if ( NULL != up )
		free ( up ) ;

}


/**************************************************************************************************/
/*  jjy_receive - receive data from the serial interface                                          */
/**************************************************************************************************/
static void
jjy_receive ( struct recvbuf *rbufp )
{

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

	l_fp	tRecvTimestamp;		/* arrival timestamp */
	int 	rc ;
	char	sLogText [ MAX_LOGTEXT ] ;
	int 	i, bCntrlChar ;

	/*
	 * Initialize pointers and read the timecode and timestamp
	 */
	peer = (struct peer *) rbufp->recv_srcclock ;
	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;

	/*
	 * Get next input line
	 */
	pp->lencode  = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;

	if ( up->linediscipline == LDISC_RAW ) {
		/*
		 * The reply with <STX> and <ETX> may give a blank line
		 */
		if ( pp->lencode == 0  &&  up->charcount == 0 ) return ;
		/*
		 * Copy received charaters to temporary buffer 
		 */
		for ( i = 0 ;
		      i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ;
		      i ++ , up->charcount ++ ) {
			up->rawbuf[up->charcount] = pp->a_lastcode[i] ;
		}
		while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) {
			for ( i = 0 ; i < up->charcount - 1 ; i ++ )
				up->rawbuf[i] = up->rawbuf[i+1] ;
			up->charcount -- ;
		}
		bCntrlChar = 0 ;
		for ( i = 0 ; i < up->charcount ; i ++ ) {
			if ( up->rawbuf[i] < ' ' ) {
				bCntrlChar = 1 ;
				break ;
			}
		}
		if ( pp->lencode > 0  &&  up->linecount < up->lineexpect ) {
			if ( bCntrlChar == 0  &&
			     up->charcount < up->charexpect[up->linecount] )
				return ;
		}
		up->rawbuf[up->charcount] = 0 ;
	} else {
		/*
		 * The reply with <CR><LF> gives a blank line
		 */
		if ( pp->lencode == 0 ) return ;
	}
	/*
	 * We get down to business
	 */

	pp->lastrec = tRecvTimestamp ;

	up->linecount ++ ;

	if ( up->lineerror != 0 ) return ;

	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 ;

	default :
		rc = 0 ;
		break ;

	}

	if ( up->linediscipline == LDISC_RAW ) {
		if ( up->linecount <= up->lineexpect  &&
		     up->charcount > up->charexpect[up->linecount-1] ) {
			for ( i = 0 ;
			      i < up->charcount - up->charexpect[up->linecount-1] ;
			      i ++ ) {
				up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ;
			}
			up->charcount -= up->charexpect[up->linecount-1] ;
		} else {
			up->charcount = 0 ;
		}
	}

	if ( rc == 0 ) 
		return ;

	up->bPollFlag = 0 ;

	if ( up->lineerror != 0 ) {
		refclock_report ( peer, CEVNT_BADREPLY ) ;
		strncpy  ( sLogText, "BAD REPLY [",
			   sizeof( sLogText ) ) ;
		if ( up->linediscipline == LDISC_RAW ) {
			strncat ( sLogText, up->rawbuf,
				  sizeof( sLogText ) -
				      strlen ( sLogText ) - 1 ) ;
		} else {
			strncat ( sLogText, pp->a_lastcode,
				  sizeof( sLogText ) -
				      strlen ( sLogText ) - 1 ) ;
		}
		sLogText[MAX_LOGTEXT-1] = 0 ;
		if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 )
			strncat ( sLogText, "]",
				  sizeof( sLogText ) -
				      strlen ( sLogText ) - 1 ) ;
		record_clock_stats ( &peer->srcadr, sLogText ) ;
		return ;
	}

	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 ) ;
		}
	}
#ifdef DEBUG
	if ( debug ) {
		printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d.%1d JST   ", 
			  up->year, up->month, up->day, up->hour,
			  up->minute, up->second, up->msecond/100 ) ;
		printf ( "( %04d/%03d %02d:%02d:%02d.%1d UTC )\n",
			  pp->year, pp->day, pp->hour, pp->minute,
			  pp->second, (int)(pp->nsec/100000000) ) ;
	}
#endif

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

	snprintf ( sLogText, sizeof(sLogText),
		   "%04d/%02d/%02d %02d:%02d:%02d.%1d JST",
		   up->year, up->month, up->day,
		   up->hour, up->minute, up->second, up->msecond/100 ) ;
	record_clock_stats ( &peer->srcadr, sLogText ) ;

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

	pp->lastref = pp->lastrec;
	refclock_receive(peer);

}

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

static int
jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
{

	static	char	*sFunctionName = "jjy_receive_tristate_jjy01" ;

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

	char	*pBuf ;
	int 	iLen ;
	int 	rc ;

	int 	bOverMidnight = 0 ;

	char	sLogText [ MAX_LOGTEXT ], sReplyText  [ MAX_LOGTEXT ] ;

	char	*pCmd ;
	int 	iCmdLen ;

	/*
	 * Initialize pointers and read the timecode and timestamp
	 */
	peer = (struct peer *) rbufp->recv_srcclock ;
	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;

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

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

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

		if ( iLen != TS_JJY01_REPLY_LENGTH_DATE ) {
			up->lineerror = 1 ;
			break ;
		}

		rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year,
			      &up->month, &up->day ) ;
		if ( rc != 3 || up->year < 2000 || up->month < 1 ||
		     up->month > 12 || up->day < 1 || up->day > 31 ) {
			up->lineerror = 1 ;
			break ;
		}

		/*** Start of modification on 2004/10/31 ***/
		/*
		 * Following codes are moved from the function jjy_poll_tristate_jjy01 in this source.
		 * The Tristate JJY-01 ( Firmware version 1.01 ) accepts "time" and "stim" commands without any delay.
		 * But the JJY-01 ( Firmware version 2.01 ) does not accept these commands continuously,
		 * so this driver issues the second command "stim" after the reply of the first command "date".
		 */

		/*** 2010/11/20 ***/
		/*
		 * Codes of a next command issue are moved to the end of this function.
		 */

		/*** End of modification ***/

		break ;

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

		if ( iLen != TS_JJY01_REPLY_LENGTH_STIM ) {
			up->lineerror = 1 ;
			break ;
		}

		rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour,
			      &up->minute, &up->second ) ;
		if ( rc != 3 || up->hour > 23 || up->minute > 59 ||
		     up->second > 60 ) {
			up->lineerror = 1 ;
			break ;
		}

		up->msecond = 0 ;
		if ( up->hour == 0 && up->minute == 0 &&
		     up->second <= 2 ) {
			/*
			 * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver separately,
			 * and the JJY receiver replies a date and time separately.
			 * Just after midnight transitions, we ignore this time.
			 */
			bOverMidnight = 1 ;
		}
		break ;

	case TS_JJY01_COMMAND_NUMBER_STUS :

		if ( ( iLen == TS_JJY01_REPLY_LENGTH_STUS_YES
		    && strncmp( pBuf, TS_JJY01_REPLY_STUS_YES,
				TS_JJY01_REPLY_LENGTH_STUS_YES ) == 0 )
		  || ( iLen == TS_JJY01_REPLY_LENGTH_STUS_NO
		    && strncmp( pBuf, TS_JJY01_REPLY_STUS_NO,
				TS_JJY01_REPLY_LENGTH_STUS_NO ) == 0 ) ) {
			/* Good */
		} else {
			up->lineerror = 1 ;
			break ;
		}

		break ;

	case TS_JJY01_COMMAND_NUMBER_DCST :

		if ( ( iLen == TS_JJY01_REPLY_LENGTH_DCST_VALID
		    && strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID,
				TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0 )
		  || ( iLen == TS_JJY01_REPLY_LENGTH_DCST_INVALID
		    && strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID,
				TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) ) {
			/* Good */
		} else {
			up->lineerror = 1 ;
			break ;
		}

		break ;

	default : /*  Unexpected reply */

		up->lineerror = 1 ;
		break ;

	}

	/* Clockstats Log */

	printableString( sReplyText, sizeof(sReplyText), pBuf, iLen ) ;
	snprintf ( sLogText, sizeof(sLogText), "%d: %s -> %c: %s",
		   up->linecount,
		   tristate_jjy01_command_sequence[up->linecount-1].commandLog,
		   ( up->lineerror == 0 ) 
			? ( ( bOverMidnight == 0 )
				? 'O' 
				: 'S' ) 
			: 'X',
		   sReplyText ) ;
	record_clock_stats ( &peer->srcadr, sLogText ) ;

	/* Check before issue next command */

	if ( up->lineerror != 0 ) {
		/* Do not issue next command */
		return 0 ;
	}

	if ( bOverMidnight != 0 ) {
		/* Do not issue next command */
		return 0 ;
	}

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

	/* Issue next command */

#ifdef DEBUG
	if ( debug ) {
		printf ( "%s (refclock_jjy.c) : send '%s'\n",
			sFunctionName, tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
	}
#endif

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

	return 0 ;

}

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

static int
jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
{

	static	char	*sFunctionName = "jjy_receive_cdex_jst2000" ;

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

	char	*pBuf ;
	int 	iLen ;
	int 	rc ;

	/*
	 * Initialize pointers and read the timecode and timestamp
	 */
	peer = (struct peer *) rbufp->recv_srcclock ;
	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;

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

	switch ( up->linecount ) {

	case 1 : /* JYYMMDD HHMMSSS */

		if ( iLen != 15 ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
					 sFunctionName, iLen ) ;
			}
#endif
			up->lineerror = 1 ;
			break ;
		}
		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 ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d.%1d ]\n",
					 sFunctionName, rc, up->year,
					 up->month, up->day, up->hour,
					 up->minute, up->second,
					 up->msecond ) ;
			}
#endif
			up->lineerror = 1 ;
			break ;
		}
		up->year    += 2000 ;
		up->msecond *= 100 ;
		break ;

	default : /*  Unexpected reply */

		up->lineerror = 1 ;
		break ;

	}

	return 1 ;

}

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

static int
jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
{

	static	char	*sFunctionName = "jjy_receive_echokeisokuki_lt2000" ;

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

	char	*pBuf ;
	int 	iLen ;
	int 	rc ;
	int	i, ibcc, ibcc1, ibcc2 ;

	/*
	 * Initialize pointers and read the timecode and timestamp
	 */
	peer = (struct peer *) rbufp->recv_srcclock ;
	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;

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

	switch ( up->linecount ) {

	case 1 : /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */

		if ( ( up->operationmode == 1 && iLen != 15 ) ||
		     ( up->operationmode == 2 && iLen != 17 ) ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
					 sFunctionName, iLen ) ;
			}
#endif
			if ( up->operationmode == 1 ) {
#ifdef DEBUG
				if ( debug ) {
					printf ( "%s (refclock_jjy.c) : send '#'\n", sFunctionName ) ;
				}
#endif
				if ( write ( pp->io.fd, "#",1 ) != 1  ) {
					refclock_report ( peer, CEVNT_FAULT ) ;
				}
			}
			up->lineerror = 1 ;
			break ;
		}

		if ( up->operationmode == 1 ) {

			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 ) {
#ifdef DEBUG
				if ( debug >= 2 ) {
					printf ( "%s (refclock_jjy.c) : BCC error ( Recv=%02X,%02X / Calc=%02X,%02X)\n",
						 sFunctionName,
						 pBuf[13] & 0xFF,
						 pBuf[14] & 0xFF,
						 ibcc1, ibcc2 ) ;
				}
#endif
				up->lineerror = 1 ;
				break ;
			}

		}

		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 ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d ]\n",
					 sFunctionName, rc, up->year,
					 up->month, up->day, up->hour,
					 up->minute, up->second ) ;
			}
#endif
			up->lineerror = 1 ;
			break ;
		}

		up->year += 2000 ;

		if ( up->operationmode == 2 ) {

			/* A time stamp comes on every 0.5 seccond in the mode 2 of the LT-2000. */
			up->msecond = 500 ;
			pp->second -- ;
			if ( pp->second < 0 ) {
				pp->second = 59 ;
				pp->minute -- ;
				if ( pp->minute < 0 ) {
					pp->minute = 59 ;
					pp->hour -- ;
					if ( pp->hour < 0 ) {
						pp->hour = 23 ;
						pp->day -- ;
						if ( pp->day < 1 ) {
							pp->year -- ;
							pp->day  = ymd2yd ( pp->year, 12, 31 ) ;
						}
					}
				}
			}

			/* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */
#ifdef DEBUG
			if ( debug ) {
				printf ( "%s (refclock_jjy.c) : send '#'\n",
					 sFunctionName ) ;
			}
#endif
			if ( write ( pp->io.fd, "#",1 ) != 1  ) {
				refclock_report ( peer, CEVNT_FAULT ) ;
			}

		}

		break ;

	default : /*  Unexpected reply */

#ifdef DEBUG
		if ( debug ) {
			printf ( "%s (refclock_jjy.c) : send '#'\n",
				 sFunctionName ) ;
		}
#endif
		if ( write ( pp->io.fd, "#",1 ) != 1  ) {
			refclock_report ( peer, CEVNT_FAULT ) ;
		}

		up->lineerror = 1 ;
		break ;

	}

	return 1 ;

}

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

static int
jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp )
{

	static char *sFunctionName = "jjy_receive_citizentic_jjy200" ;

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

	char	*pBuf ;
	int	iLen ;
	int	rc ;
	char	cApostrophe, sStatus[3] ;
	int	iWeekday ;

	/*
	* Initialize pointers and read the timecode and timestamp
	*/
	peer = (struct peer *) rbufp->recv_srcclock ;
	pp = peer->procptr ;
	up = (struct jjyunit *) pp->unitptr ;

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

	/*
	* JJY-200 sends a timestamp every second.
	* So, a timestamp is ignored unless it is right after polled.
	*/
	if ( ! up->bPollFlag )
		return 0 ;

	switch ( up->linecount ) {

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

		if ( iLen != 23 ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
					 sFunctionName, iLen ) ;
			}
#endif
			up->lineerror = 1 ;
			break ;
		}

		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 || up->month < 1 ||
		     up->month > 12 || up->day < 1 || up->day > 31 ||
		     iWeekday > 6 || up->hour > 23 || up->minute > 59 ||
		     up->second > 60 ) {
#ifdef DEBUG
			if ( debug >= 2 ) {
				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %c %2s %02d %02d %02d %d %02d %02d %02d ]\n",
					 sFunctionName, rc, cApostrophe,
					 sStatus, up->year, up->month,
					 up->day, iWeekday, up->hour,
					 up->minute, up->second ) ;
			}
#endif
			up->lineerror = 1 ;
			break ;
		}

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

		break ;

	default : /* Unexpected reply */

		up->lineerror = 1 ;
		break ;

	}

	return 1 ;

}

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

	struct jjyunit      *up;
	struct refclockproc *pp;

	pp = peer->procptr;
	up = (struct jjyunit *) pp->unitptr ;

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

#ifdef DEBUG
	if ( debug ) {
		printf ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ;
	}
#endif

	pp->polls ++ ;

	up->bPollFlag = 1 ;
	up->linecount = 0 ;
	up->lineerror = 0 ;
	up->charcount = 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 ;

	default :
		break ;

	}

}

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

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

	static char *sFunctionName = "jjy_poll_tristate_jjy01" ;

	struct jjyunit	    *up;
	struct refclockproc *pp;

	char	*pCmd ;
	int 	iCmdLen ;

	pp = peer->procptr;
	up = (struct jjyunit *) pp->unitptr ;

	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
		up->linecount = 2 ;
	}

#ifdef DEBUG
	if ( debug ) {
		printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->linecount=%d\n",
			sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
			up->linecount ) ;
	}
#endif

	/*
	 * Send a first command
	 */

#ifdef DEBUG
	if ( debug ) {
		printf ( "%s (refclock_jjy.c) : send '%s'\n",
			 sFunctionName,
			 tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
	}
#endif

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

}

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

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

	struct refclockproc *pp;

	pp = peer->procptr;

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

#ifdef DEBUG
	if ( debug ) {
		printf ( "jjy_poll_cdex_jst2000 (refclock_jjy.c) : send '<ENQ>1J<ETX>'\n" ) ;
	}
#endif

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

}

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

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

	struct jjyunit      *up;
	struct refclockproc *pp;

	char	sCmd[2] ;

	pp = peer->procptr;
	up = (struct jjyunit *) pp->unitptr ;

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

	switch ( up->operationmode ) {
	case 1 : sCmd[0] = 'T' ; break ;
	case 2 : sCmd[0] = 'C' ; break ;
	}
	sCmd[1] = 0 ;

#ifdef DEBUG
	if ( debug ) {
		printf ( "jjy_poll_echokeisokuki_lt2000 (refclock_jjy.c) : send '%s'\n", sCmd ) ;
	}
#endif

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

}

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

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

	/* Do nothing ( up->bPollFlag is set by the jjy_poll ) */

}

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

static void
printableString ( char *sOutput, int iOutputLen, char *sInput, int iInputLen )
{

	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>" ,
			" " } ;

	int	i, j, n ;

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

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

}

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

#else
int refclock_jjy_bs ;
#endif /* REFCLOCK */
