refclock_jjy.c revision 280849
1/*
2 * refclock_jjy - clock driver for JJY receivers
3 */
4
5/**********************************************************************/
6/*								      */
7/*  Copyright (C) 2001-2011, Takao Abe.  All rights reserved.	      */
8/*								      */
9/*  Permission to use, copy, modify, and distribute this software     */
10/*  and its documentation for any purpose is hereby granted	      */
11/*  without fee, provided that the following conditions are met:      */
12/*								      */
13/*  One retains the entire copyright notice properly, and both the    */
14/*  copyright notice and this license. in the documentation and/or    */
15/*  other materials provided with the distribution.		      */
16/*								      */
17/*  This software and the name of the author must not be used to      */
18/*  endorse or promote products derived from this software without    */
19/*  prior written permission.					      */
20/*								      */
21/*  THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESSED OR IMPLIED    */
22/*  WARRANTIES OF ANY KIND, INCLUDING, BUT NOT LIMITED TO, THE	      */
23/*  IMPLIED WARRANTIES OF MERCHANTABLILITY AND FITNESS FOR A	      */
24/*  PARTICULAR PURPOSE.						      */
25/*  IN NO EVENT SHALL THE AUTHOR TAKAO ABE BE LIABLE FOR ANY DIRECT,  */
26/*  INDIRECT, GENERAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES   */
27/*  ( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE	      */
28/*  GOODS OR SERVICES; LOSS OF USE, DATA OR PROFITS; OR BUSINESS      */
29/*  INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,     */
30/*  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ( INCLUDING	      */
31/*  NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF    */
32/*  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
33/*								      */
34/*  This driver is developed in my private time, and is opened as     */
35/*  voluntary contributions for the NTP.			      */
36/*  The manufacturer of the JJY receiver has not participated in      */
37/*  a development of this driver.				      */
38/*  The manufacturer does not warrant anything about this driver,     */
39/*  and is not liable for anything about this driver.		      */
40/*								      */
41/**********************************************************************/
42/*								      */
43/*  Author     Takao Abe					      */
44/*  Email      takao_abe@xurb.jp				      */
45/*  Homepage   http://www.bea.hi-ho.ne.jp/abetakao/		      */
46/*								      */
47/*  The email address abetakao@bea.hi-ho.ne.jp is never read	      */
48/*  from 2010, because a few filtering rule are provided by the	      */
49/*  "hi-ho.ne.jp", and lots of spam mail are reached.		      */
50/*  New email address for supporting the refclock_jjy is	      */
51/*  takao_abe@xurb.jp						      */
52/*								      */
53/**********************************************************************/
54/*								      */
55/*  History							      */
56/*								      */
57/*  2001/07/15							      */
58/*    [New]    Support the Tristate Ltd. JJY receiver		      */
59/*								      */
60/*  2001/08/04							      */
61/*    [Change] Log to clockstats even if bad reply		      */
62/*    [Fix]    PRECISION = (-3) (about 100 ms)			      */
63/*    [Add]    Support the C-DEX Co.Ltd. JJY receiver		      */
64/*								      */
65/*  2001/12/04							      */
66/*    [Fix]    C-DEX JST2000 ( fukusima@goto.info.waseda.ac.jp )      */
67/*								      */
68/*  2002/07/12							      */
69/*    [Fix]    Portability for FreeBSD ( patched by the user )	      */
70/*								      */
71/*  2004/10/31							      */
72/*    [Change] Command send timing for the Tristate Ltd. JJY receiver */
73/*	       JJY-01 ( Firmware version 2.01 )			      */
74/*	       Thanks to Andy Taki for testing under FreeBSD	      */
75/*								      */
76/*  2004/11/28							      */
77/*    [Add]    Support the Echo Keisokuki LT-2000 receiver	      */
78/*								      */
79/*  2006/11/04							      */
80/*    [Fix]    C-DEX JST2000					      */
81/*	       Thanks to Hideo Kuramatsu for the patch		      */
82/*								      */
83/*  2009/04/05							      */
84/*    [Add]    Support the CITIZEN T.I.C JJY-200 receiver	      */
85/*								      */
86/*  2010/11/20							      */
87/*    [Change] Bug 1618 ( Harmless )				      */
88/*	       Code clean up ( Remove unreachable codes ) in	      */
89/*	       jjy_start()					      */
90/*    [Change] Change clockstats format of the Tristate JJY01/02      */
91/*	       Issues more command to get the status of the receiver  */
92/*	       when "fudge 127.127.40.X flag1 1" is specified	      */
93/*	       ( DATE,STIM -> DCST,STUS,DATE,STIM )		      */
94/*								      */
95/*  2011/04/30							      */
96/*    [Add]    Support the Tristate Ltd. TS-GPSclock-01		      */
97/*								      */
98/**********************************************************************/
99
100#ifdef HAVE_CONFIG_H
101#include <config.h>
102#endif
103
104#if defined(REFCLOCK) && defined(CLOCK_JJY)
105
106#include <stdio.h>
107#include <ctype.h>
108#include <string.h>
109#include <sys/time.h>
110#include <time.h>
111
112#include "ntpd.h"
113#include "ntp_io.h"
114#include "ntp_tty.h"
115#include "ntp_refclock.h"
116#include "ntp_calendar.h"
117#include "ntp_stdlib.h"
118
119/**********************************************************************/
120/*								      */
121/*  The Tristate Ltd. JJY receiver JJY01			      */
122/*								      */
123/*  Command	   Response		    Remarks		      */
124/*  ------------   ----------------------   ---------------------     */
125/*  dcst<CR><LF>   VALID|INVALID<CR><LF>			      */
126/*  stus<CR><LF>   ADJUSTED|UNADJUSTED<CR><LF>			      */
127/*  date<CR><LF>   YYYY/MM/DD XXX<CR><LF>			      */
128/*  time<CR><LF>   HH:MM:SS<CR><LF>	    Not used by this driver   */
129/*  stim<CR><LF>   HH:MM:SS<CR><LF>	    Reply at just second      */
130/*								      */
131/*  During synchronization after a receiver is turned on,	      */
132/*  It replies the past time from 2000/01/01 00:00:00.		      */
133/*  The function "refclock_process" checks the time and tells	      */
134/*  as an insanity time.					      */
135/*								      */
136/**********************************************************************/
137/*								      */
138/*  The C-DEX Co. Ltd. JJY receiver JST2000			      */
139/*								      */
140/*  Command	   Response		    Remarks		      */
141/*  ------------   ----------------------   ---------------------     */
142/*  <ENQ>1J<ETX>   <STX>JYYMMDD HHMMSSS<ETX>			      */
143/*								      */
144/**********************************************************************/
145/*								      */
146/*  The Echo Keisokuki Co. Ltd. JJY receiver LT2000		      */
147/*								      */
148/*  Command        Response		    Remarks		      */
149/*  ------------   ----------------------   ---------------------     */
150/*  #					    Mode 1 (Request&Send)     */
151/*  T		   YYMMDDWHHMMSS<BCC1><BCC2><CR>		      */
152/*  C					    Mode 2 (Continuous)	      */
153/*		   YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR>	      */
154/*		   <SUB>		    Second signal	      */
155/*								      */
156/**********************************************************************/
157/*								      */
158/*  The CITIZEN T.I.C CO., LTD. JJY receiver JJY200		      */
159/*								      */
160/*  Command	   Response		    Remarks		      */
161/*  ------------   ----------------------   ---------------------     */
162/*		   'XX YY/MM/DD W HH:MM:SS<CR>			      */
163/*					    XX: OK|NG|ER	      */
164/*					    W:  0(Monday)-6(Sunday)   */
165/*								      */
166/**********************************************************************/
167/*								      */
168/*  The Tristate Ltd. GPS clock TS-GPSCLOCK-01			      */
169/*								      */
170/*  This clock has NMEA mode and command/respose mode.		      */
171/*  When this jjy driver are used, set to command/respose mode        */
172/*  of this clock by the onboard switch SW4, and make sure the        */
173/*  LED-Y is tured on.						      */
174/*  Other than this JJY driver, the refclock driver type 20,	      */
175/*  generic NMEA driver, works with the NMEA mode of this clock.      */
176/*								      */
177/*  Command	   Response		    Remarks		      */
178/*  ------------   ----------------------   ---------------------     */
179/*  stus<CR><LF>   *R|*G|*U|+U<CR><LF>				      */
180/*  date<CR><LF>   YY/MM/DD<CR><LF>				      */
181/*  time<CR><LF>   HH:MM:SS<CR><LF>				      */
182/*								      */
183/**********************************************************************/
184
185/*
186 * Interface definitions
187 */
188#define	DEVICE  	"/dev/jjy%d"	/* device name and unit */
189#define	SPEED232	B9600		/* uart speed (9600 baud) */
190#define	SPEED232_TRISTATE_JJY01		B9600   /* UART speed (9600 baud) */
191#define	SPEED232_CDEX_JST2000		B9600   /* UART speed (9600 baud) */
192#define	SPEED232_ECHOKEISOKUKI_LT2000	B9600   /* UART speed (9600 baud) */
193#define	SPEED232_CITIZENTIC_JJY200	B4800   /* UART speed (4800 baud) */
194#define	SPEED232_TRISTATE_GPSCLOCK01	B38400  /* USB  speed (38400 baud) */
195#define	REFID   	"JJY"		/* reference ID */
196#define	DESCRIPTION	"JJY Receiver"
197#define	PRECISION	(-3)		/* precision assumed (about 100 ms) */
198
199/*
200 * JJY unit control structure
201 */
202struct jjyunit {
203	char	unittype ;	    /* UNITTYPE_XXXXXXXXXX */
204	short   operationmode ;	    /* Echo Keisokuki LT-2000 : 1 or 2 */
205	short	version ;
206	short	linediscipline ;    /* LDISC_CLK or LDISC_RAW */
207	char    bPollFlag ;	    /* Set by jjy_pool and Reset by jjy_receive */
208	int 	linecount ;
209	int 	lineerror ;
210	int 	year, month, day, hour, minute, second, msecond ;
211/* LDISC_RAW only */
212#define	MAX_LINECOUNT	8
213#define	MAX_RAWBUF   	64
214	int 	lineexpect ;
215	int 	charexpect [ MAX_LINECOUNT ] ;
216	int 	charcount ;
217	char	rawbuf [ MAX_RAWBUF ] ;
218};
219
220#define	UNITTYPE_TRISTATE_JJY01		1
221#define	UNITTYPE_CDEX_JST2000		2
222#define	UNITTYPE_ECHOKEISOKUKI_LT2000  	3
223#define	UNITTYPE_CITIZENTIC_JJY200  	4
224#define	UNITTYPE_TRISTATE_GPSCLOCK01	5
225
226/*
227 * Function prototypes
228 */
229
230static	int 	jjy_start			(int, struct peer *);
231static	void	jjy_shutdown			(int, struct peer *);
232
233static	void	jjy_poll		    	(int, struct peer *);
234static	void	jjy_poll_tristate_jjy01	    	(int, struct peer *);
235static	void	jjy_poll_cdex_jst2000	    	(int, struct peer *);
236static	void	jjy_poll_echokeisokuki_lt2000	(int, struct peer *);
237static	void	jjy_poll_citizentic_jjy200	(int, struct peer *);
238static	void	jjy_poll_tristate_gpsclock01	(int, struct peer *);
239
240static	void	jjy_receive			(struct recvbuf *);
241static	int	jjy_receive_tristate_jjy01	(struct recvbuf *);
242static	int	jjy_receive_cdex_jst2000	(struct recvbuf *);
243static	int	jjy_receive_echokeisokuki_lt2000 (struct recvbuf *);
244static  int	jjy_receive_citizentic_jjy200	(struct recvbuf *);
245static	int	jjy_receive_tristate_gpsclock01	(struct recvbuf *);
246
247static	void	printableString ( char*, int, char*, int ) ;
248
249/*
250 * Transfer vector
251 */
252struct	refclock refclock_jjy = {
253	jjy_start,	/* start up driver */
254	jjy_shutdown,	/* shutdown driver */
255	jjy_poll,	/* transmit poll message */
256	noentry,	/* not used */
257	noentry,	/* not used */
258	noentry,	/* not used */
259	NOFLAGS		/* not used */
260};
261
262/*
263 * Start up driver return code
264 */
265#define	RC_START_SUCCESS	1
266#define	RC_START_ERROR		0
267
268/*
269 * Local constants definition
270 */
271
272#define	MAX_LOGTEXT	64
273
274/*
275 * Tristate JJY01/JJY02 constants definition
276 */
277
278#define	TS_JJY01_COMMAND_NUMBER_DATE	1
279#define	TS_JJY01_COMMAND_NUMBER_TIME	2
280#define	TS_JJY01_COMMAND_NUMBER_STIM	3
281#define	TS_JJY01_COMMAND_NUMBER_STUS	4
282#define	TS_JJY01_COMMAND_NUMBER_DCST	5
283
284#define	TS_JJY01_REPLY_DATE		"yyyy/mm/dd www\r\n"
285#define	TS_JJY01_REPLY_STIM		"hh:mm:ss\r\n"
286#define	TS_JJY01_REPLY_STUS_YES		"adjusted\r\n"
287#define	TS_JJY01_REPLY_STUS_NO		"unadjusted\r\n"
288#define	TS_JJY01_REPLY_DCST_VALID	"valid\r\n"
289#define	TS_JJY01_REPLY_DCST_INVALID	"invalid\r\n"
290
291#define	TS_JJY01_REPLY_LENGTH_DATE	    14	/* Length without <CR><LF> */
292#define	TS_JJY01_REPLY_LENGTH_STIM	    8	/* Length without <CR><LF> */
293#define	TS_JJY01_REPLY_LENGTH_STUS_YES	    8	/* Length without <CR><LF> */
294#define	TS_JJY01_REPLY_LENGTH_STUS_NO	    10	/* Length without <CR><LF> */
295#define	TS_JJY01_REPLY_LENGTH_DCST_VALID    5	/* Length without <CR><LF> */
296#define	TS_JJY01_REPLY_LENGTH_DCST_INVALID  7	/* Length without <CR><LF> */
297
298static  struct
299{
300	const char	commandNumber ;
301	const char	*commandLog ;
302	const char	*command ;
303	int	commandLength ;
304} tristate_jjy01_command_sequence[] =
305{
306	/* dcst<CR><LF> -> VALID<CR><LF> or INVALID<CR><LF> */
307	{ TS_JJY01_COMMAND_NUMBER_DCST, "dcst", "dcst\r\n", 6 },
308	/* stus<CR><LF> -> ADJUSTED<CR><LF> or UNADJUSTED<CR><LF> */
309	{ TS_JJY01_COMMAND_NUMBER_STUS, "stus", "stus\r\n", 6 },
310	/* date<CR><LF> -> YYYY/MM/DD WWW<CR><LF> */
311	{ TS_JJY01_COMMAND_NUMBER_DATE, "date", "date\r\n", 6 },
312	/* stim<CR><LF> -> HH:MM:SS<CR><LF> */
313	{ TS_JJY01_COMMAND_NUMBER_STIM, "stim", "stim\r\n", 6 },
314	/* End of command */
315	{ 0, NULL, NULL, 0 }
316} ;
317
318/*
319 * Tristate TS-GPSCLOCK01 constants definition
320 */
321
322#define	TS_GPSCLOCK01_COMMAND_NUMBER_DATE	1
323#define	TS_GPSCLOCK01_COMMAND_NUMBER_TIME	2
324#define	TS_GPSCLOCK01_COMMAND_NUMBER_STUS	4
325
326#define	TS_GPSCLOCK01_REPLY_DATE		"yyyy/mm/dd\r\n"
327#define	TS_GPSCLOCK01_REPLY_TIME		"hh:mm:ss\r\n"
328#define	TS_GPSCLOCK01_REPLY_STUS_RTC		"*R\r\n"
329#define	TS_GPSCLOCK01_REPLY_STUS_GPS		"*G\r\n"
330#define	TS_GPSCLOCK01_REPLY_STUS_UTC		"*U\r\n"
331#define	TS_GPSCLOCK01_REPLY_STUS_PPS		"+U\r\n"
332
333#define	TS_GPSCLOCK01_REPLY_LENGTH_DATE	    10	/* Length without <CR><LF> */
334#define	TS_GPSCLOCK01_REPLY_LENGTH_TIME	    8	/* Length without <CR><LF> */
335#define	TS_GPSCLOCK01_REPLY_LENGTH_STUS	    2	/* Length without <CR><LF> */
336
337static  struct
338{
339	char	commandNumber ;
340	const char	*commandLog ;
341	const char	*command ;
342	int	commandLength ;
343} tristate_gpsclock01_command_sequence[] =
344{
345	/* stus<CR><LF> -> *R<CR><LF> or *G<CR><LF> or *U<CR><LF> or +U<CR><LF> */
346	{ TS_GPSCLOCK01_COMMAND_NUMBER_STUS, "stus", "stus\r\n", 6 },
347	/* date<CR><LF> -> YYYY/MM/DD WWW<CR><LF> */
348	{ TS_GPSCLOCK01_COMMAND_NUMBER_DATE, "date", "date\r\n", 6 },
349	/* time<CR><LF> -> HH:MM:SS<CR><LF> */
350	{ TS_GPSCLOCK01_COMMAND_NUMBER_TIME, "time", "time\r\n", 6 },
351	/* End of command */
352	{ 0, NULL, NULL, 0 }
353} ;
354
355/**************************************************************************************************/
356/*  jjy_start - open the devices and initialize data for processing                               */
357/**************************************************************************************************/
358static int
359jjy_start ( int unit, struct peer *peer )
360{
361
362	struct jjyunit	    *up ;
363	struct refclockproc *pp ;
364	int 	fd ;
365	char	*pDeviceName ;
366	short	iDiscipline ;
367	int 	iSpeed232 ;
368
369	char	sLogText [ MAX_LOGTEXT ] , sDevText [ MAX_LOGTEXT ] ;
370
371#ifdef DEBUG
372	if ( debug ) {
373		printf ( "jjy_start (refclock_jjy.c) : %s  mode=%d  ", ntoa(&peer->srcadr), peer->ttl ) ;
374		printf ( DEVICE, unit ) ;
375		printf ( "\n" ) ;
376	}
377#endif
378	snprintf ( sDevText, sizeof(sDevText), DEVICE, unit ) ;
379	snprintf ( sLogText, sizeof(sLogText), "*Initialze*  %s  mode=%d", sDevText, peer->ttl ) ;
380	record_clock_stats ( &peer->srcadr, sLogText ) ;
381
382	/*
383	 * Open serial port
384	 */
385	pDeviceName = emalloc ( strlen(DEVICE) + 10 );
386	snprintf ( pDeviceName, strlen(DEVICE) + 10, DEVICE, unit ) ;
387
388	/*
389	 * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
390	 */
391	switch ( peer->ttl ) {
392	case 0 :
393	case 1 :
394		iDiscipline = LDISC_CLK ;
395		iSpeed232   = SPEED232_TRISTATE_JJY01 ;
396		break ;
397	case 2 :
398		iDiscipline = LDISC_RAW ;
399		iSpeed232   = SPEED232_CDEX_JST2000   ;
400		break ;
401	case 3 :
402		iDiscipline = LDISC_CLK ;
403		iSpeed232   = SPEED232_ECHOKEISOKUKI_LT2000 ;
404		break ;
405	case 4 :
406		iDiscipline = LDISC_CLK ;
407		iSpeed232   = SPEED232_CITIZENTIC_JJY200 ;
408		break ;
409	case 5 :
410		iDiscipline = LDISC_CLK ;
411		iSpeed232   = SPEED232_TRISTATE_GPSCLOCK01 ;
412		break ;
413	default :
414		msyslog ( LOG_ERR, "JJY receiver [ %s mode %d ] : Unsupported mode",
415			  ntoa(&peer->srcadr), peer->ttl ) ;
416		free ( (void*) pDeviceName ) ;
417		return RC_START_ERROR ;
418	}
419
420	fd = refclock_open ( pDeviceName, iSpeed232, iDiscipline ) ;
421	if ( fd <= 0 ) {
422		free ( (void*) pDeviceName ) ;
423		return RC_START_ERROR ;
424	}
425	free ( (void*) pDeviceName ) ;
426
427	/*
428	 * Allocate and initialize unit structure
429	 */
430	up = emalloc (sizeof(*up));
431	memset ( up, 0, sizeof(*up) ) ;
432	up->linediscipline = iDiscipline ;
433
434	/*
435	 * peer->ttl is a mode number specified by "127.127.40.X mode N" in the ntp.conf
436	 */
437	switch ( peer->ttl ) {
438	case 0 :
439		/*
440		 * The mode 0 is a default clock type at this time.
441		 * But this will be change to auto-detect mode in the future.
442		 */
443	case 1 :
444		up->unittype = UNITTYPE_TRISTATE_JJY01 ;
445		up->version  = 100 ;
446		/* 2010/11/20 */
447		/* Command sequence is defined by the struct tristate_jjy01_command_sequence, */
448		/* and the following 3 lines are not used in the mode LDISC_CLK. */
449		/* up->lineexpect = 2 ; */
450		/* up->charexpect[0] = 14 ; */ /* YYYY/MM/DD WWW<CR><LF> */
451		/* up->charexpect[1] =  8 ; */ /* HH:MM:SS<CR><LF> */
452		break ;
453	case 2 :
454		up->unittype = UNITTYPE_CDEX_JST2000 ;
455		up->lineexpect = 1 ;
456		up->charexpect[0] = 15 ; /* <STX>JYYMMDD HHMMSSS<ETX> */
457		break ;
458	case 3 :
459		up->unittype = UNITTYPE_ECHOKEISOKUKI_LT2000 ;
460		up->operationmode = 2 ;  /* Mode 2 : Continuous mode */
461		up->lineexpect = 1 ;
462		switch ( up->operationmode ) {
463		case 1 :
464			up->charexpect[0] = 15 ; /* YYMMDDWHHMMSS<BCC1><BCC2><CR> */
465			break ;
466		case 2 :
467			up->charexpect[0] = 17 ; /* YYMMDDWHHMMSS<ST1><ST2><ST3><ST4><CR> */
468			break ;
469		}
470		break ;
471	case 4 :
472		up->unittype = UNITTYPE_CITIZENTIC_JJY200 ;
473		up->lineexpect = 1 ;
474		up->charexpect[0] = 23 ; /* 'XX YY/MM/DD W HH:MM:SS<CR> */
475		break ;
476	case 5 :
477		up->unittype = UNITTYPE_TRISTATE_GPSCLOCK01 ;
478		break ;
479
480	/* 2010/11/20 */
481	/* The "default:" section of this switch block is never executed,     */
482	/* because the former switch block traps the same "default:" case.    */
483	/* This "default:" section codes are removed to avoid spending time   */
484	/* in the future looking, though the codes are functionally harmless. */
485
486	}
487
488	pp = peer->procptr ;
489	pp->unitptr       = up ;
490	pp->io.clock_recv = jjy_receive ;
491	pp->io.srcclock   = peer ;
492	pp->io.datalen	  = 0 ;
493	pp->io.fd	  = fd ;
494	if ( ! io_addclock(&pp->io) ) {
495		close ( fd ) ;
496		pp->io.fd = -1 ;
497		free ( up ) ;
498		pp->unitptr = NULL ;
499		return RC_START_ERROR ;
500	}
501
502	/*
503	 * Initialize miscellaneous variables
504	 */
505	peer->precision = PRECISION ;
506	pp->clockdesc	= DESCRIPTION ;
507	memcpy ( (char*)&pp->refid, REFID, strlen(REFID) ) ;
508
509	return RC_START_SUCCESS ;
510
511}
512
513
514/**************************************************************************************************/
515/*  jjy_shutdown - shutdown the clock                                                             */
516/**************************************************************************************************/
517static void
518jjy_shutdown ( int unit, struct peer *peer )
519{
520
521	struct jjyunit	    *up;
522	struct refclockproc *pp;
523
524	pp = peer->procptr ;
525	up = pp->unitptr ;
526	if ( -1 != pp->io.fd )
527		io_closeclock ( &pp->io ) ;
528	if ( NULL != up )
529		free ( up ) ;
530
531}
532
533
534/**************************************************************************************************/
535/*  jjy_receive - receive data from the serial interface                                          */
536/**************************************************************************************************/
537static void
538jjy_receive ( struct recvbuf *rbufp )
539{
540
541	struct jjyunit	    *up ;
542	struct refclockproc *pp ;
543	struct peer	    *peer;
544
545	l_fp	tRecvTimestamp;		/* arrival timestamp */
546	int 	rc ;
547	char	sLogText [ MAX_LOGTEXT ] ;
548	int 	i, bCntrlChar ;
549
550	/*
551	 * Initialize pointers and read the timecode and timestamp
552	 */
553	peer = rbufp->recv_peer ;
554	pp = peer->procptr ;
555	up = pp->unitptr ;
556
557	/*
558	 * Get next input line
559	 */
560	pp->lencode  = refclock_gtlin ( rbufp, pp->a_lastcode, BMAX, &tRecvTimestamp ) ;
561
562	if ( up->linediscipline == LDISC_RAW ) {
563		/*
564		 * The reply with <STX> and <ETX> may give a blank line
565		 */
566		if ( pp->lencode == 0  &&  up->charcount == 0 ) return ;
567		/*
568		 * Copy received charaters to temporary buffer
569		 */
570		for ( i = 0 ;
571		      i < pp->lencode && up->charcount < MAX_RAWBUF - 2 ;
572		      i ++ , up->charcount ++ ) {
573			up->rawbuf[up->charcount] = pp->a_lastcode[i] ;
574		}
575		while ( up->charcount > 0 && up->rawbuf[0] < ' ' ) {
576			for ( i = 0 ; i < up->charcount - 1 ; i ++ )
577				up->rawbuf[i] = up->rawbuf[i+1] ;
578			up->charcount -- ;
579		}
580		bCntrlChar = 0 ;
581		for ( i = 0 ; i < up->charcount ; i ++ ) {
582			if ( up->rawbuf[i] < ' ' ) {
583				bCntrlChar = 1 ;
584				break ;
585			}
586		}
587		if ( pp->lencode > 0  &&  up->linecount < up->lineexpect ) {
588			if ( bCntrlChar == 0  &&
589			     up->charcount < up->charexpect[up->linecount] )
590				return ;
591		}
592		up->rawbuf[up->charcount] = 0 ;
593	} else {
594		/*
595		 * The reply with <CR><LF> gives a blank line
596		 */
597		if ( pp->lencode == 0 ) return ;
598	}
599	/*
600	 * We get down to business
601	 */
602
603#ifdef DEBUG
604	if ( debug ) {
605		if ( up->linediscipline == LDISC_RAW ) {
606			printableString( sLogText, MAX_LOGTEXT, up->rawbuf, up->charcount ) ;
607		} else {
608			printableString( sLogText, MAX_LOGTEXT, pp->a_lastcode, pp->lencode ) ;
609		}
610		printf ( "jjy_receive (refclock_jjy.c) : [%s]\n", sLogText ) ;
611	}
612#endif
613
614	pp->lastrec = tRecvTimestamp ;
615
616	up->linecount ++ ;
617
618	if ( up->lineerror != 0 ) return ;
619
620	switch ( up->unittype ) {
621
622	case UNITTYPE_TRISTATE_JJY01 :
623		rc = jjy_receive_tristate_jjy01  ( rbufp ) ;
624		break ;
625
626	case UNITTYPE_CDEX_JST2000 :
627		rc = jjy_receive_cdex_jst2000 ( rbufp ) ;
628		break ;
629
630	case UNITTYPE_ECHOKEISOKUKI_LT2000 :
631		rc = jjy_receive_echokeisokuki_lt2000 ( rbufp ) ;
632		break ;
633
634	case UNITTYPE_CITIZENTIC_JJY200 :
635		rc = jjy_receive_citizentic_jjy200 ( rbufp ) ;
636		break ;
637
638	case UNITTYPE_TRISTATE_GPSCLOCK01 :
639		rc = jjy_receive_tristate_gpsclock01  ( rbufp ) ;
640		break ;
641
642	default :
643		rc = 0 ;
644		break ;
645
646	}
647
648	if ( up->linediscipline == LDISC_RAW ) {
649		if ( up->linecount <= up->lineexpect  &&
650		     up->charcount > up->charexpect[up->linecount-1] ) {
651			for ( i = 0 ;
652			      i < up->charcount - up->charexpect[up->linecount-1] ;
653			      i ++ ) {
654				up->rawbuf[i] = up->rawbuf[i+up->charexpect[up->linecount-1]] ;
655			}
656			up->charcount -= up->charexpect[up->linecount-1] ;
657		} else {
658			up->charcount = 0 ;
659		}
660	}
661
662	if ( rc == 0 ) {
663		return ;
664	}
665
666	up->bPollFlag = 0 ;
667
668	if ( up->lineerror != 0 ) {
669		refclock_report ( peer, CEVNT_BADREPLY ) ;
670		strlcpy  ( sLogText, "BAD REPLY [",
671			   sizeof( sLogText ) ) ;
672		if ( up->linediscipline == LDISC_RAW ) {
673			strlcat ( sLogText, up->rawbuf,
674				  sizeof( sLogText ) ) ;
675		} else {
676			strlcat ( sLogText, pp->a_lastcode,
677				  sizeof( sLogText ) ) ;
678		}
679		sLogText[MAX_LOGTEXT-1] = 0 ;
680		if ( strlen ( sLogText ) < MAX_LOGTEXT - 2 )
681			strlcat ( sLogText, "]",
682				  sizeof( sLogText ) ) ;
683		record_clock_stats ( &peer->srcadr, sLogText ) ;
684		return ;
685	}
686
687	pp->year   = up->year ;
688	pp->day    = ymd2yd ( up->year, up->month, up->day ) ;
689	pp->hour   = up->hour ;
690	pp->minute = up->minute ;
691	pp->second = up->second ;
692	pp->nsec   = up->msecond * 1000000;
693
694	/*
695	 * JST to UTC
696	 */
697	pp->hour -= 9 ;
698	if ( pp->hour < 0 ) {
699		pp->hour += 24 ;
700		pp->day -- ;
701		if ( pp->day < 1 ) {
702			pp->year -- ;
703			pp->day  = ymd2yd ( pp->year, 12, 31 ) ;
704		}
705	}
706#ifdef DEBUG
707	if ( debug ) {
708		printf ( "jjy_receive (refclock_jjy.c) : %04d/%02d/%02d %02d:%02d:%02d.%1d JST   ",
709			  up->year, up->month, up->day, up->hour,
710			  up->minute, up->second, up->msecond/100 ) ;
711		printf ( "( %04d/%03d %02d:%02d:%02d.%1d UTC )\n",
712			  pp->year, pp->day, pp->hour, pp->minute,
713			  pp->second, (int)(pp->nsec/100000000) ) ;
714	}
715#endif
716
717	/*
718	 * Process the new sample in the median filter and determine the
719	 * timecode timestamp.
720	 */
721
722	snprintf ( sLogText, sizeof(sLogText),
723		   "%04d/%02d/%02d %02d:%02d:%02d.%1d JST",
724		   up->year, up->month, up->day,
725		   up->hour, up->minute, up->second, up->msecond/100 ) ;
726	record_clock_stats ( &peer->srcadr, sLogText ) ;
727
728	if ( ! refclock_process ( pp ) ) {
729		refclock_report(peer, CEVNT_BADTIME);
730		return ;
731	}
732
733	pp->lastref = pp->lastrec;
734	refclock_receive(peer);
735
736}
737
738/**************************************************************************************************/
739
740static int
741jjy_receive_tristate_jjy01 ( struct recvbuf *rbufp )
742{
743#ifdef DEBUG
744	static	const char	*sFunctionName = "jjy_receive_tristate_jjy01" ;
745#endif
746
747	struct jjyunit	    *up ;
748	struct refclockproc *pp ;
749	struct peer	    *peer;
750
751	char	*pBuf ;
752	int 	iLen ;
753	int 	rc ;
754
755	int 	bOverMidnight = 0 ;
756
757	char	sLogText [ MAX_LOGTEXT ], sReplyText  [ MAX_LOGTEXT ] ;
758
759	const char *pCmd ;
760	int 	iCmdLen ;
761
762	/*
763	 * Initialize pointers and read the timecode and timestamp
764	 */
765	peer = rbufp->recv_peer ;
766	pp = peer->procptr ;
767	up = pp->unitptr ;
768
769	if ( up->linediscipline == LDISC_RAW ) {
770		pBuf = up->rawbuf ;
771		iLen = up->charcount ;
772	} else {
773		pBuf = pp->a_lastcode ;
774		iLen = pp->lencode ;
775	}
776
777	switch ( tristate_jjy01_command_sequence[up->linecount-1].commandNumber ) {
778
779	case TS_JJY01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD WWW */
780
781		if ( iLen != TS_JJY01_REPLY_LENGTH_DATE ) {
782			up->lineerror = 1 ;
783			break ;
784		}
785
786		rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year,
787			      &up->month, &up->day ) ;
788		if ( rc != 3 || up->year < 2000 || up->month < 1 ||
789		     up->month > 12 || up->day < 1 || up->day > 31 ) {
790			up->lineerror = 1 ;
791			break ;
792		}
793
794		/*** Start of modification on 2004/10/31 ***/
795		/*
796		 * Following codes are moved from the function jjy_poll_tristate_jjy01 in this source.
797		 * The Tristate JJY-01 ( Firmware version 1.01 ) accepts "time" and "stim" commands without any delay.
798		 * But the JJY-01 ( Firmware version 2.01 ) does not accept these commands continuously,
799		 * so this driver issues the second command "stim" after the reply of the first command "date".
800		 */
801
802		/*** 2010/11/20 ***/
803		/*
804		 * Codes of a next command issue are moved to the end of this function.
805		 */
806
807		/*** End of modification ***/
808
809		break ;
810
811	case TS_JJY01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
812	case TS_JJY01_COMMAND_NUMBER_STIM : /* HH:MM:SS */
813
814		if ( iLen != TS_JJY01_REPLY_LENGTH_STIM ) {
815			up->lineerror = 1 ;
816			break ;
817		}
818
819		rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour,
820			      &up->minute, &up->second ) ;
821		if ( rc != 3 || up->hour > 23 || up->minute > 59 ||
822		     up->second > 60 ) {
823			up->lineerror = 1 ;
824			break ;
825		}
826
827		up->msecond = 0 ;
828		if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
829			/*
830			 * The command "date" and "time" ( or "stim" ) were sent to the JJY receiver separately,
831			 * and the JJY receiver replies a date and time separately.
832			 * Just after midnight transitions, we ignore this time.
833			 */
834			bOverMidnight = 1 ;
835		}
836		break ;
837
838	case TS_JJY01_COMMAND_NUMBER_STUS :
839
840		if ( ( iLen == TS_JJY01_REPLY_LENGTH_STUS_YES
841		    && strncmp( pBuf, TS_JJY01_REPLY_STUS_YES,
842				TS_JJY01_REPLY_LENGTH_STUS_YES ) == 0 )
843		  || ( iLen == TS_JJY01_REPLY_LENGTH_STUS_NO
844		    && strncmp( pBuf, TS_JJY01_REPLY_STUS_NO,
845				TS_JJY01_REPLY_LENGTH_STUS_NO ) == 0 ) ) {
846			/* Good */
847		} else {
848			up->lineerror = 1 ;
849			break ;
850		}
851
852		break ;
853
854	case TS_JJY01_COMMAND_NUMBER_DCST :
855
856		if ( ( iLen == TS_JJY01_REPLY_LENGTH_DCST_VALID
857		    && strncmp( pBuf, TS_JJY01_REPLY_DCST_VALID,
858				TS_JJY01_REPLY_LENGTH_DCST_VALID ) == 0 )
859		  || ( iLen == TS_JJY01_REPLY_LENGTH_DCST_INVALID
860		    && strncmp( pBuf, TS_JJY01_REPLY_DCST_INVALID,
861				TS_JJY01_REPLY_LENGTH_DCST_INVALID ) == 0 ) ) {
862			/* Good */
863		} else {
864			up->lineerror = 1 ;
865			break ;
866		}
867
868		break ;
869
870	default : /*  Unexpected reply */
871
872		up->lineerror = 1 ;
873		break ;
874
875	}
876
877	/* Clockstats Log */
878
879	printableString( sReplyText, sizeof(sReplyText), pBuf, iLen ) ;
880	snprintf ( sLogText, sizeof(sLogText), "%d: %s -> %c: %s",
881		   up->linecount,
882		   tristate_jjy01_command_sequence[up->linecount-1].commandLog,
883		   ( up->lineerror == 0 )
884			? ( ( bOverMidnight == 0 )
885				? 'O'
886				: 'S' )
887			: 'X',
888		   sReplyText ) ;
889	record_clock_stats ( &peer->srcadr, sLogText ) ;
890
891	/* Check before issue next command */
892
893	if ( up->lineerror != 0 ) {
894		/* Do not issue next command */
895		return 0 ;
896	}
897
898	if ( bOverMidnight != 0 ) {
899		/* Do not issue next command */
900		return 0 ;
901	}
902
903	if ( tristate_jjy01_command_sequence[up->linecount].command == NULL ) {
904		/* Command sequence completed */
905		return 1 ;
906	}
907
908	/* Issue next command */
909
910#ifdef DEBUG
911	if ( debug ) {
912		printf ( "%s (refclock_jjy.c) : send '%s'\n",
913			sFunctionName, tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
914	}
915#endif
916
917	pCmd =  tristate_jjy01_command_sequence[up->linecount].command ;
918	iCmdLen = tristate_jjy01_command_sequence[up->linecount].commandLength ;
919	if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
920		refclock_report ( peer, CEVNT_FAULT ) ;
921	}
922
923	return 0 ;
924
925}
926
927/**************************************************************************************************/
928
929static int
930jjy_receive_cdex_jst2000 ( struct recvbuf *rbufp )
931{
932#ifdef DEBUG
933	static	const char	*sFunctionName = "jjy_receive_cdex_jst2000" ;
934#endif
935
936	struct jjyunit      *up ;
937	struct refclockproc *pp ;
938	struct peer         *peer;
939
940	char	*pBuf ;
941	int 	iLen ;
942	int 	rc ;
943
944	/*
945	 * Initialize pointers and read the timecode and timestamp
946	 */
947	peer = rbufp->recv_peer ;
948	pp = peer->procptr ;
949	up = pp->unitptr ;
950
951	if ( up->linediscipline == LDISC_RAW ) {
952		pBuf = up->rawbuf ;
953		iLen = up->charcount ;
954	} else {
955		pBuf = pp->a_lastcode ;
956		iLen = pp->lencode ;
957	}
958
959	switch ( up->linecount ) {
960
961	case 1 : /* JYYMMDD HHMMSSS */
962
963		if ( iLen != 15 ) {
964#ifdef DEBUG
965			if ( debug >= 2 ) {
966				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
967					 sFunctionName, iLen ) ;
968			}
969#endif
970			up->lineerror = 1 ;
971			break ;
972		}
973		rc = sscanf ( pBuf, "J%2d%2d%2d%*1d%2d%2d%2d%1d",
974			      &up->year, &up->month, &up->day,
975			      &up->hour, &up->minute, &up->second,
976			      &up->msecond ) ;
977		if ( rc != 7 || up->month < 1 || up->month > 12 ||
978		     up->day < 1 || up->day > 31 || up->hour > 23 ||
979		     up->minute > 59 || up->second > 60 ) {
980#ifdef DEBUG
981			if ( debug >= 2 ) {
982				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d.%1d ]\n",
983					 sFunctionName, rc, up->year,
984					 up->month, up->day, up->hour,
985					 up->minute, up->second,
986					 up->msecond ) ;
987			}
988#endif
989			up->lineerror = 1 ;
990			break ;
991		}
992		up->year    += 2000 ;
993		up->msecond *= 100 ;
994		break ;
995
996	default : /*  Unexpected reply */
997
998		up->lineerror = 1 ;
999		break ;
1000
1001	}
1002
1003	return 1 ;
1004
1005}
1006
1007/**************************************************************************************************/
1008
1009static int
1010jjy_receive_echokeisokuki_lt2000 ( struct recvbuf *rbufp )
1011{
1012#ifdef DEBUG
1013	static	const char	*sFunctionName = "jjy_receive_echokeisokuki_lt2000" ;
1014#endif
1015
1016	struct jjyunit      *up ;
1017	struct refclockproc *pp ;
1018	struct peer	    *peer;
1019
1020	char	*pBuf ;
1021	int 	iLen ;
1022	int 	rc ;
1023	int	i, ibcc, ibcc1, ibcc2 ;
1024
1025	/*
1026	 * Initialize pointers and read the timecode and timestamp
1027	 */
1028	peer = rbufp->recv_peer ;
1029	pp = peer->procptr ;
1030	up = pp->unitptr ;
1031
1032	if ( up->linediscipline == LDISC_RAW ) {
1033		pBuf = up->rawbuf ;
1034		iLen = up->charcount ;
1035	} else {
1036		pBuf = pp->a_lastcode ;
1037		iLen = pp->lencode ;
1038	}
1039
1040	switch ( up->linecount ) {
1041
1042	case 1 : /* YYMMDDWHHMMSS<BCC1><BCC2> or YYMMDDWHHMMSS<ST1><ST2><ST3><ST4> */
1043
1044		if ( ( up->operationmode == 1 && iLen != 15 ) ||
1045		     ( up->operationmode == 2 && iLen != 17 ) ) {
1046#ifdef DEBUG
1047			if ( debug >= 2 ) {
1048				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
1049					 sFunctionName, iLen ) ;
1050			}
1051#endif
1052			if ( up->operationmode == 1 ) {
1053#ifdef DEBUG
1054				if ( debug ) {
1055					printf ( "%s (refclock_jjy.c) : send '#'\n", __func__ ) ;
1056				}
1057#endif
1058				if ( write ( pp->io.fd, "#",1 ) != 1  ) {
1059					refclock_report ( peer, CEVNT_FAULT ) ;
1060				}
1061			}
1062			up->lineerror = 1 ;
1063			break ;
1064		}
1065
1066		if ( up->operationmode == 1 ) {
1067
1068			for ( i = ibcc = 0 ; i < 13 ; i ++ )
1069				ibcc ^= pBuf[i] ;
1070			ibcc1 = 0x30 | ( ( ibcc >> 4 ) & 0xF ) ;
1071			ibcc2 = 0x30 | ( ( ibcc      ) & 0xF ) ;
1072			if ( pBuf[13] != ibcc1 || pBuf[14] != ibcc2 ) {
1073#ifdef DEBUG
1074				if ( debug >= 2 ) {
1075					printf ( "%s (refclock_jjy.c) : BCC error ( Recv=%02X,%02X / Calc=%02X,%02X)\n",
1076						 sFunctionName,
1077						 pBuf[13] & 0xFF,
1078						 pBuf[14] & 0xFF,
1079						 ibcc1, ibcc2 ) ;
1080				}
1081#endif
1082				up->lineerror = 1 ;
1083				break ;
1084			}
1085
1086		}
1087
1088		rc = sscanf ( pBuf, "%2d%2d%2d%*1d%2d%2d%2d",
1089			      &up->year, &up->month, &up->day,
1090			      &up->hour, &up->minute, &up->second ) ;
1091		if ( rc != 6 || up->month < 1 || up->month > 12 ||
1092		     up->day < 1 || up->day > 31 || up->hour > 23 ||
1093		     up->minute > 59 || up->second > 60 ) {
1094#ifdef DEBUG
1095			if ( debug >= 2 ) {
1096				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %02d %02d %02d * %02d %02d %02d ]\n",
1097					 sFunctionName, rc, up->year,
1098					 up->month, up->day, up->hour,
1099					 up->minute, up->second ) ;
1100			}
1101#endif
1102			up->lineerror = 1 ;
1103			break ;
1104		}
1105
1106		up->year += 2000 ;
1107
1108		if ( up->operationmode == 2 ) {
1109
1110			/* A time stamp comes on every 0.5 seccond in the mode 2 of the LT-2000. */
1111			up->msecond = 500 ;
1112			pp->second -- ;
1113			if ( pp->second < 0 ) {
1114				pp->second = 59 ;
1115				pp->minute -- ;
1116				if ( pp->minute < 0 ) {
1117					pp->minute = 59 ;
1118					pp->hour -- ;
1119					if ( pp->hour < 0 ) {
1120						pp->hour = 23 ;
1121						pp->day -- ;
1122						if ( pp->day < 1 ) {
1123							pp->year -- ;
1124							pp->day  = ymd2yd ( pp->year, 12, 31 ) ;
1125						}
1126					}
1127				}
1128			}
1129
1130			/* Switch from mode 2 to mode 1 in order to restraint of useless time stamp. */
1131#ifdef DEBUG
1132			if ( debug ) {
1133				printf ( "%s (refclock_jjy.c) : send '#'\n",
1134					 sFunctionName ) ;
1135			}
1136#endif
1137			if ( write ( pp->io.fd, "#",1 ) != 1  ) {
1138				refclock_report ( peer, CEVNT_FAULT ) ;
1139			}
1140
1141		}
1142
1143		break ;
1144
1145	default : /*  Unexpected reply */
1146
1147#ifdef DEBUG
1148		if ( debug ) {
1149			printf ( "%s (refclock_jjy.c) : send '#'\n",
1150				 sFunctionName ) ;
1151		}
1152#endif
1153		if ( write ( pp->io.fd, "#",1 ) != 1  ) {
1154			refclock_report ( peer, CEVNT_FAULT ) ;
1155		}
1156
1157		up->lineerror = 1 ;
1158		break ;
1159
1160	}
1161
1162	return 1 ;
1163
1164}
1165
1166/**************************************************************************************************/
1167
1168static int
1169jjy_receive_citizentic_jjy200 ( struct recvbuf *rbufp )
1170{
1171#ifdef DEBUG
1172	static const char *sFunctionName = "jjy_receive_citizentic_jjy200" ;
1173#endif
1174
1175	struct jjyunit		*up ;
1176	struct refclockproc	*pp ;
1177	struct peer		*peer;
1178
1179	char	*pBuf ;
1180	int	iLen ;
1181	int	rc ;
1182	char	cApostrophe, sStatus[3] ;
1183	int	iWeekday ;
1184
1185	/*
1186	* Initialize pointers and read the timecode and timestamp
1187	*/
1188	peer = rbufp->recv_peer ;
1189	pp = peer->procptr ;
1190	up = pp->unitptr ;
1191
1192	if ( up->linediscipline == LDISC_RAW ) {
1193		pBuf = up->rawbuf ;
1194		iLen = up->charcount ;
1195	} else {
1196		pBuf = pp->a_lastcode ;
1197		iLen = pp->lencode ;
1198	}
1199
1200	/*
1201	* JJY-200 sends a timestamp every second.
1202	* So, a timestamp is ignored unless it is right after polled.
1203	*/
1204	if ( ! up->bPollFlag )
1205		return 0 ;
1206
1207	switch ( up->linecount ) {
1208
1209	case 1 : /* 'XX YY/MM/DD W HH:MM:SS<CR> */
1210
1211		if ( iLen != 23 ) {
1212#ifdef DEBUG
1213			if ( debug >= 2 ) {
1214				printf ( "%s (refclock_jjy.c) : Reply length error ( iLen=%d )\n",
1215					 sFunctionName, iLen ) ;
1216			}
1217#endif
1218			up->lineerror = 1 ;
1219			break ;
1220		}
1221
1222		rc = sscanf ( pBuf, "%c%2s %2d/%2d/%2d %1d %2d:%2d:%2d",
1223			      &cApostrophe, sStatus, &up->year,
1224			      &up->month, &up->day, &iWeekday,
1225			      &up->hour, &up->minute, &up->second ) ;
1226		sStatus[2] = 0 ;
1227		if ( rc != 9 || cApostrophe != '\'' ||
1228		     strcmp( sStatus, "OK" ) != 0 || up->month < 1 ||
1229		     up->month > 12 || up->day < 1 || up->day > 31 ||
1230		     iWeekday > 6 || up->hour > 23 || up->minute > 59 ||
1231		     up->second > 60 ) {
1232#ifdef DEBUG
1233			if ( debug >= 2 ) {
1234				printf ( "%s (refclock_jjy.c) : Time error (rc=%d) [ %c %2s %02d %02d %02d %d %02d %02d %02d ]\n",
1235					 sFunctionName, rc, cApostrophe,
1236					 sStatus, up->year, up->month,
1237					 up->day, iWeekday, up->hour,
1238					 up->minute, up->second ) ;
1239			}
1240#endif
1241			up->lineerror = 1 ;
1242			break ;
1243		}
1244
1245		up->year += 2000 ;
1246		up->msecond = 0 ;
1247
1248		break ;
1249
1250	default : /* Unexpected reply */
1251
1252		up->lineerror = 1 ;
1253		break ;
1254
1255	}
1256
1257	return 1 ;
1258
1259}
1260
1261/**************************************************************************************************/
1262
1263static int
1264jjy_receive_tristate_gpsclock01 ( struct recvbuf *rbufp )
1265{
1266#ifdef DEBUG
1267	static	const char	*sFunctionName = "jjy_receive_tristate_gpsclock01" ;
1268#endif
1269
1270	struct jjyunit	    *up ;
1271	struct refclockproc *pp ;
1272	struct peer	    *peer;
1273
1274	char	*pBuf ;
1275	int 	iLen ;
1276	int 	rc ;
1277
1278	int 	bOverMidnight = 0 ;
1279
1280	char	sLogText [ MAX_LOGTEXT ], sReplyText [ MAX_LOGTEXT ] ;
1281
1282	const char	*pCmd ;
1283	int 	iCmdLen ;
1284
1285	/*
1286	 * Initialize pointers and read the timecode and timestamp
1287	 */
1288	peer = rbufp->recv_peer ;
1289	pp = peer->procptr ;
1290	up = pp->unitptr ;
1291
1292	if ( up->linediscipline == LDISC_RAW ) {
1293		pBuf = up->rawbuf ;
1294		iLen = up->charcount ;
1295	} else {
1296		pBuf = pp->a_lastcode ;
1297		iLen = pp->lencode ;
1298	}
1299
1300	/*
1301	 * Ignore NMEA data stream
1302	 */
1303	if ( iLen > 5
1304	  && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
1305#ifdef DEBUG
1306		if ( debug ) {
1307			printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
1308				sFunctionName, pBuf ) ;
1309		}
1310#endif
1311		return 0 ;
1312	}
1313
1314	/*
1315	 * Skip command prompt '$Cmd>' from the TS-GPSclock-01
1316	 */
1317	if ( iLen == 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
1318		return 0 ;
1319	} else if ( iLen > 5 && strncmp( pBuf, "$Cmd>", 5 ) == 0 ) {
1320		pBuf += 5 ;
1321		iLen -= 5 ;
1322	}
1323
1324	/*
1325	 * Ignore NMEA data stream after command prompt
1326	 */
1327	if ( iLen > 5
1328	  && ( strncmp( pBuf, "$GP", 3 ) == 0 || strncmp( pBuf, "$PFEC", 5 ) == 0 ) ) {
1329#ifdef DEBUG
1330		if ( debug ) {
1331			printf ( "%s (refclock_jjy.c) : Skip NMEA stream [%s]\n",
1332				sFunctionName, pBuf ) ;
1333		}
1334#endif
1335		return 0 ;
1336	}
1337
1338	switch ( tristate_gpsclock01_command_sequence[up->linecount-1].commandNumber ) {
1339
1340	case TS_GPSCLOCK01_COMMAND_NUMBER_DATE : /* YYYY/MM/DD */
1341
1342		if ( iLen != TS_GPSCLOCK01_REPLY_LENGTH_DATE ) {
1343			up->lineerror = 1 ;
1344			break ;
1345		}
1346
1347		rc = sscanf ( pBuf, "%4d/%2d/%2d", &up->year, &up->month, &up->day ) ;
1348		if ( rc != 3 || up->year < 2000 || up->month < 1 || up->month > 12 ||
1349		     up->day < 1 || up->day > 31 ) {
1350			up->lineerror = 1 ;
1351			break ;
1352		}
1353
1354		break ;
1355
1356	case TS_GPSCLOCK01_COMMAND_NUMBER_TIME : /* HH:MM:SS */
1357
1358		if ( iLen != TS_GPSCLOCK01_REPLY_LENGTH_TIME ) {
1359			up->lineerror = 1 ;
1360			break ;
1361		}
1362
1363		rc = sscanf ( pBuf, "%2d:%2d:%2d", &up->hour, &up->minute, &up->second ) ;
1364		if ( rc != 3 || up->hour > 23 || up->minute > 59 || up->second > 60 ) {
1365			up->lineerror = 1 ;
1366			break ;
1367		}
1368
1369		up->msecond = 0 ;
1370
1371		if ( up->hour == 0 && up->minute == 0 && up->second <= 2 ) {
1372			/*
1373			 * The command "date" and "time" were sent to the JJY receiver separately,
1374			 * and the JJY receiver replies a date and time separately.
1375			 * Just after midnight transitions, we ignore this time.
1376			 */
1377			bOverMidnight = 1 ;
1378		}
1379
1380		break ;
1381
1382	case TS_GPSCLOCK01_COMMAND_NUMBER_STUS :
1383
1384		if ( iLen == TS_GPSCLOCK01_REPLY_LENGTH_STUS
1385		  && ( strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_RTC, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
1386		    || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_GPS, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
1387		    || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_UTC, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0
1388		    || strncmp( pBuf, TS_GPSCLOCK01_REPLY_STUS_PPS, TS_GPSCLOCK01_REPLY_LENGTH_STUS ) == 0 ) ) {
1389			/* Good */
1390		} else {
1391			up->lineerror = 1 ;
1392			break ;
1393		}
1394
1395		break ;
1396
1397	default : /*  Unexpected reply */
1398
1399		up->lineerror = 1 ;
1400		break ;
1401
1402	}
1403
1404	/* Clockstats Log */
1405
1406	printableString( sReplyText, sizeof(sReplyText), pBuf, iLen ) ;
1407	snprintf ( sLogText, sizeof(sLogText), "%d: %s -> %c: %s",
1408		   up->linecount,
1409		   tristate_gpsclock01_command_sequence[up->linecount-1].commandLog,
1410		   ( up->lineerror == 0 )
1411			? ( ( bOverMidnight == 0 )
1412				? 'O'
1413				: 'S' )
1414			: 'X',
1415		   sReplyText ) ;
1416	record_clock_stats ( &peer->srcadr, sLogText ) ;
1417
1418	/* Check before issue next command */
1419
1420	if ( up->lineerror != 0 ) {
1421		/* Do not issue next command */
1422		return 0 ;
1423	}
1424
1425	if ( bOverMidnight != 0 ) {
1426		/* Do not issue next command */
1427		return 0 ;
1428	}
1429
1430	if ( tristate_gpsclock01_command_sequence[up->linecount].command == NULL ) {
1431		/* Command sequence completed */
1432		return 1 ;
1433	}
1434
1435	/* Issue next command */
1436
1437#ifdef DEBUG
1438	if ( debug ) {
1439		printf ( "%s (refclock_jjy.c) : send '%s'\n",
1440			sFunctionName, tristate_gpsclock01_command_sequence[up->linecount].commandLog ) ;
1441	}
1442#endif
1443
1444	pCmd =  tristate_gpsclock01_command_sequence[up->linecount].command ;
1445	iCmdLen = tristate_gpsclock01_command_sequence[up->linecount].commandLength ;
1446	if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
1447		refclock_report ( peer, CEVNT_FAULT ) ;
1448	}
1449
1450	return 0 ;
1451
1452}
1453
1454/**************************************************************************************************/
1455/*  jjy_poll - called by the transmit procedure                                                   */
1456/**************************************************************************************************/
1457static void
1458jjy_poll ( int unit, struct peer *peer )
1459{
1460
1461	struct jjyunit      *up;
1462	struct refclockproc *pp;
1463
1464	pp = peer->procptr;
1465	up = pp->unitptr ;
1466
1467	if ( pp->polls > 0  &&  up->linecount == 0 ) {
1468		/*
1469		 * No reply for last command
1470		 */
1471		refclock_report ( peer, CEVNT_TIMEOUT ) ;
1472	}
1473
1474#ifdef DEBUG
1475	if ( debug ) {
1476		printf ( "jjy_poll (refclock_jjy.c) : %ld\n", pp->polls ) ;
1477	}
1478#endif
1479
1480	pp->polls ++ ;
1481
1482	up->bPollFlag = 1 ;
1483	up->linecount = 0 ;
1484	up->lineerror = 0 ;
1485	up->charcount = 0 ;
1486
1487	switch ( up->unittype ) {
1488
1489	case UNITTYPE_TRISTATE_JJY01 :
1490		jjy_poll_tristate_jjy01  ( unit, peer ) ;
1491		break ;
1492
1493	case UNITTYPE_CDEX_JST2000 :
1494		jjy_poll_cdex_jst2000 ( unit, peer ) ;
1495		break ;
1496
1497	case UNITTYPE_ECHOKEISOKUKI_LT2000 :
1498		jjy_poll_echokeisokuki_lt2000 ( unit, peer ) ;
1499		break ;
1500
1501	case UNITTYPE_CITIZENTIC_JJY200 :
1502		jjy_poll_citizentic_jjy200 ( unit, peer ) ;
1503		break ;
1504
1505	case UNITTYPE_TRISTATE_GPSCLOCK01 :
1506		jjy_poll_tristate_gpsclock01  ( unit, peer ) ;
1507		break ;
1508
1509	default :
1510		break ;
1511
1512	}
1513
1514}
1515
1516/**************************************************************************************************/
1517
1518static void
1519jjy_poll_tristate_jjy01  ( int unit, struct peer *peer )
1520{
1521#ifdef DEBUG
1522	static const char *sFunctionName = "jjy_poll_tristate_jjy01" ;
1523#endif
1524
1525	struct jjyunit	    *up;
1526	struct refclockproc *pp;
1527
1528	const char *pCmd ;
1529	int 	iCmdLen ;
1530
1531	pp = peer->procptr;
1532	up = pp->unitptr ;
1533
1534	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
1535		up->linecount = 2 ;
1536	}
1537
1538#ifdef DEBUG
1539	if ( debug ) {
1540		printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->linecount=%d\n",
1541			sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
1542			up->linecount ) ;
1543	}
1544#endif
1545
1546	/*
1547	 * Send a first command
1548	 */
1549
1550#ifdef DEBUG
1551	if ( debug ) {
1552		printf ( "%s (refclock_jjy.c) : send '%s'\n",
1553			 sFunctionName,
1554			 tristate_jjy01_command_sequence[up->linecount].commandLog ) ;
1555	}
1556#endif
1557
1558	pCmd =  tristate_jjy01_command_sequence[up->linecount].command ;
1559	iCmdLen = tristate_jjy01_command_sequence[up->linecount].commandLength ;
1560	if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
1561		refclock_report ( peer, CEVNT_FAULT ) ;
1562	}
1563
1564}
1565
1566/**************************************************************************************************/
1567
1568static void
1569jjy_poll_cdex_jst2000 ( int unit, struct peer *peer )
1570{
1571
1572	struct refclockproc *pp;
1573
1574	pp = peer->procptr;
1575
1576	/*
1577	 * Send "<ENQ>1J<ETX>" command
1578	 */
1579
1580#ifdef DEBUG
1581	if ( debug ) {
1582		printf ( "jjy_poll_cdex_jst2000 (refclock_jjy.c) : send '<ENQ>1J<ETX>'\n" ) ;
1583	}
1584#endif
1585
1586	if ( write ( pp->io.fd, "\0051J\003", 4 ) != 4  ) {
1587		refclock_report ( peer, CEVNT_FAULT ) ;
1588	}
1589
1590}
1591
1592/**************************************************************************************************/
1593
1594static void
1595jjy_poll_echokeisokuki_lt2000 ( int unit, struct peer *peer )
1596{
1597
1598	struct jjyunit      *up;
1599	struct refclockproc *pp;
1600
1601	char	sCmd[2] ;
1602
1603	pp = peer->procptr;
1604	up = pp->unitptr ;
1605
1606	/*
1607	 * Send "T" or "C" command
1608	 */
1609
1610	switch ( up->operationmode ) {
1611	case 1 : sCmd[0] = 'T' ; break ;
1612	case 2 : sCmd[0] = 'C' ; break ;
1613	}
1614	sCmd[1] = 0 ;
1615
1616#ifdef DEBUG
1617	if ( debug ) {
1618		printf ( "jjy_poll_echokeisokuki_lt2000 (refclock_jjy.c) : send '%s'\n", sCmd ) ;
1619	}
1620#endif
1621
1622	if ( write ( pp->io.fd, sCmd, 1 ) != 1  ) {
1623		refclock_report ( peer, CEVNT_FAULT ) ;
1624	}
1625
1626}
1627
1628/**************************************************************************************************/
1629
1630static void
1631jjy_poll_citizentic_jjy200 ( int unit, struct peer *peer )
1632{
1633
1634	/* Do nothing ( up->bPollFlag is set by the jjy_poll ) */
1635
1636}
1637
1638/**************************************************************************************************/
1639
1640static void
1641jjy_poll_tristate_gpsclock01  ( int unit, struct peer *peer )
1642{
1643#ifdef DEBUG
1644	static const char *sFunctionName = "jjy_poll_tristate_gpsclock01" ;
1645#endif
1646
1647	struct jjyunit	    *up;
1648	struct refclockproc *pp;
1649
1650	const char	*pCmd ;
1651	int 	iCmdLen ;
1652
1653	pp = peer->procptr;
1654	up = pp->unitptr ;
1655
1656	if ( ( pp->sloppyclockflag & CLK_FLAG1 ) == 0 ) {
1657		up->linecount = 1 ;
1658	}
1659
1660#ifdef DEBUG
1661	if ( debug ) {
1662		printf ( "%s (refclock_jjy.c) : flag1=%X CLK_FLAG1=%X up->linecount=%d\n",
1663			sFunctionName, pp->sloppyclockflag, CLK_FLAG1,
1664			up->linecount ) ;
1665	}
1666#endif
1667
1668	/*
1669	 * Send a first command
1670	 */
1671
1672#ifdef DEBUG
1673	if ( debug ) {
1674		printf ( "%s (refclock_jjy.c) : send '%s'\n",
1675			 sFunctionName,
1676			 tristate_gpsclock01_command_sequence[up->linecount].commandLog ) ;
1677	}
1678#endif
1679
1680	pCmd =  tristate_gpsclock01_command_sequence[up->linecount].command ;
1681	iCmdLen = tristate_gpsclock01_command_sequence[up->linecount].commandLength ;
1682	if ( write ( pp->io.fd, pCmd, iCmdLen ) != iCmdLen ) {
1683		refclock_report ( peer, CEVNT_FAULT ) ;
1684	}
1685
1686}
1687
1688/**************************************************************************************************/
1689
1690static void
1691printableString ( char *sOutput, int iOutputLen, char *sInput, int iInputLen )
1692{
1693	const char	*printableControlChar[] = {
1694			"<NUL>", "<SOH>", "<STX>", "<ETX>",
1695			"<EOT>", "<ENQ>", "<ACK>", "<BEL>",
1696			"<BS>" , "<HT>" , "<LF>" , "<VT>" ,
1697			"<FF>" , "<CR>" , "<SO>" , "<SI>" ,
1698			"<DLE>", "<DC1>", "<DC2>", "<DC3>",
1699			"<DC4>", "<NAK>", "<SYN>", "<ETB>",
1700			"<CAN>", "<EM>" , "<SUB>", "<ESC>",
1701			"<FS>" , "<GS>" , "<RS>" , "<US>" ,
1702			" " } ;
1703
1704	size_t	i, j, n ;
1705	size_t	InputLen;
1706	size_t	OutputLen;
1707
1708	InputLen = (size_t)iInputLen;
1709	OutputLen = (size_t)iOutputLen;
1710	for ( i = j = 0 ; i < InputLen && j < OutputLen ; i ++ ) {
1711		if ( isprint( (unsigned char)sInput[i] ) ) {
1712			n = 1 ;
1713			if ( j + 1 >= OutputLen )
1714				break ;
1715			sOutput[j] = sInput[i] ;
1716		} else if ( ( sInput[i] & 0xFF ) <
1717			    COUNTOF(printableControlChar) ) {
1718			n = strlen( printableControlChar[sInput[i] & 0xFF] ) ;
1719			if ( j + n + 1 >= OutputLen )
1720				break ;
1721			strlcpy( sOutput + j,
1722				 printableControlChar[sInput[i] & 0xFF],
1723				 OutputLen - j ) ;
1724		} else {
1725			n = 5 ;
1726			if ( j + n + 1 >= OutputLen )
1727				break ;
1728			snprintf( sOutput + j, OutputLen - j, "<x%X>",
1729				  sInput[i] & 0xFF ) ;
1730		}
1731		j += n ;
1732	}
1733
1734	sOutput[min(j, (size_t)iOutputLen - 1)] = '\0' ;
1735
1736}
1737
1738/**************************************************************************************************/
1739
1740#else
1741int refclock_jjy_bs ;
1742#endif /* REFCLOCK */
1743