xref: /freebsd/contrib/ntp/libparse/clk_meinberg.c (revision a151a66c)
1 /*
2  * /src/NTP/ntp-4/libparse/clk_meinberg.c,v 4.8 1999/11/28 09:13:50 kardel RELEASE_19991128_A
3  *
4  * clk_meinberg.c,v 4.8 1999/11/28 09:13:50 kardel RELEASE_19991128_A
5  *
6  * Meinberg clock support
7  *
8  * Copyright (C) 1995-1999 by Frank Kardel <kardel@acm.org>
9  * Copyright (C) 1992-1994 by Frank Kardel, Friedrich-Alexander Universit�t Erlangen-N�rnberg, Germany
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_MEINBERG)
22 
23 #include <sys/types.h>
24 #include <sys/time.h>
25 
26 #include "ntp_fp.h"
27 #include "ntp_unixtime.h"
28 #include "ntp_calendar.h"
29 
30 #include "ntp_machine.h"
31 
32 #include "parse.h"
33 
34 #ifndef PARSESTREAM
35 #include <stdio.h>
36 #else
37 #include "sys/parsestreams.h"
38 #endif
39 
40 #include "ntp_stdlib.h"
41 
42 #include "ntp_stdlib.h"
43 
44 #include "mbg_gps166.h"
45 #include "binio.h"
46 #include "ascii.h"
47 
48 /*
49  * The Meinberg receiver every second sends a datagram of the following form
50  * (Standard Format)
51  *
52  *     <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
53  * pos:  0  00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2  2  3  3   3
54  *       1  23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8  9  0  1   2
55  * <STX>           = '\002' ASCII start of text
56  * <ETX>           = '\003' ASCII end of text
57  * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
58  * <w>             = day of week (sunday= 0)
59  * <hh>,<mm>,<ss>  = hour, minute, second
60  * <S>             = '#' if never synced since powerup for DCF C51
61  *                 = '#' if not PZF sychronisation available for PZF 535/509
62  *                 = ' ' if ok
63  * <F>             = '*' if time comes from internal quartz
64  *                 = ' ' if completely synched
65  * <D>             = 'S' if daylight saving time is active
66  *                 = 'U' if time is represented in UTC
67  *                 = ' ' if no special condition exists
68  * <A>             = '!' during the hour preceeding an daylight saving time
69  *                       start/end change
70  *                 = 'A' leap second insert warning
71  *                 = ' ' if no special condition exists
72  *
73  * Extended data format (PZFUERL for PZF type clocks)
74  *
75  *     <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
76  * pos:  0   00 0 00 0 00 11 1 11 11 1 11 2 22 22 2  2  2  2  2  3  3   3
77  *       1   23 4 56 7 89 01 2 34 56 7 89 0 12 34 5  6  7  8  9  0  1   2
78  * <STX>           = '\002' ASCII start of text
79  * <ETX>           = '\003' ASCII end of text
80  * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
81  * <w>             = day of week (sunday= 0)
82  * <hh>,<mm>,<ss>  = hour, minute, second
83  * <U>             = 'U' UTC time display
84  * <S>             = '#' if never synced since powerup else ' ' for DCF C51
85  *                   '#' if not PZF sychronisation available else ' ' for PZF 535/509
86  * <F>             = '*' if time comes from internal quartz else ' '
87  * <D>             = 'S' if daylight saving time is active else ' '
88  * <A>             = '!' during the hour preceeding an daylight saving time
89  *                       start/end change
90  * <L>             = 'A' LEAP second announcement
91  * <R>             = 'R' alternate antenna
92  *
93  * Meinberg GPS166 receiver
94  *
95  * You must get the Uni-Erlangen firmware for the GPS receiver support
96  * to work to full satisfaction !
97  *
98  *     <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
99  *
100  *        000000000111111111122222222223333333333444444444455555555556666666
101  *        123456789012345678901234567890123456789012345678901234567890123456
102  *     \x0209.07.93; 5; 08:48:26; +00:00; #*S!A L; 49.5736N  11.0280E  373m\x03
103  *
104  *
105  * <STX>           = '\002' ASCII start of text
106  * <ETX>           = '\003' ASCII end of text
107  * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
108  * <w>             = day of week (sunday= 0)
109  * <hh>,<mm>,<ss>  = hour, minute, second
110  * <+/->,<00:00>   = offset to UTC
111  * <S>             = '#' if never synced since powerup else ' '
112  * <F>             = '*' if position is not confirmed else ' '
113  * <D>             = 'S' if daylight saving time is active else ' '
114  * <A>             = '!' during the hour preceeding an daylight saving time
115  *                       start/end change
116  * <L>             = 'A' LEAP second announcement
117  * <R>             = 'R' alternate antenna (reminiscent of PZF535) usually ' '
118  * <L>		   = 'L' on 23:59:60
119  *
120  * Binary messages have a lead in for a fixed header of SOH
121  */
122 
123 /*--------------------------------------------------------------*/
124 /* Name:         csum()                                         */
125 /*                                                              */
126 /* Purpose:      Compute a checksum about a number of bytes     */
127 /*                                                              */
128 /* Input:        uchar *p    address of the first byte          */
129 /*               short n     the number of bytes                */
130 /*                                                              */
131 /* Output:       --                                             */
132 /*                                                              */
133 /* Ret val:      the checksum                                   */
134 /*+-------------------------------------------------------------*/
135 
136 unsigned long
137 mbg_csum(
138 	 unsigned char *p,
139 	 unsigned int n
140 	 )
141 {
142   unsigned long sum = 0;
143   short i;
144 
145   for ( i = 0; i < n; i++ )
146     sum += *p++;
147 
148   return( sum );
149 }  /* csum */
150 
151 void
152 get_mbg_header(
153 	       unsigned char **bufpp,
154 	       GPS_MSG_HDR *headerp
155 	       )
156 {
157   headerp->gps_cmd = get_lsb_short(bufpp);
158   headerp->gps_len = get_lsb_short(bufpp);
159   headerp->gps_data_csum = get_lsb_short(bufpp);
160   headerp->gps_hdr_csum  = get_lsb_short(bufpp);
161 }
162 
163 static struct format meinberg_fmt[] =
164 {
165 	{
166 		{
167 			{ 3, 2},  {  6, 2}, {  9, 2},
168 			{ 18, 2}, { 21, 2}, { 24, 2},
169 			{ 14, 1}, { 27, 4}, { 29, 1},
170 		},
171 		(const unsigned char *)"\2D:  .  .  ;T: ;U:  .  .  ;    \3",
172 		0
173 	},
174 	{			/* special extended FAU Erlangen extended format */
175 		{
176 			{ 1, 2},  { 4,  2}, {  7, 2},
177 			{ 14, 2}, { 17, 2}, { 20, 2},
178 			{ 11, 1}, { 25, 4}, { 27, 1},
179 		},
180 		(const unsigned char *)"\2  .  .  ;  ;   :  :  ;        \3",
181 		MBG_EXTENDED
182 	},
183 	{			/* special extended FAU Erlangen GPS format */
184 		{
185 			{ 1,  2}, {  4, 2}, {  7, 2},
186 			{ 14, 2}, { 17, 2}, { 20, 2},
187 			{ 11, 1}, { 32, 7}, { 35, 1},
188 			{ 25, 2}, { 28, 2}, { 24, 1}
189 		},
190 		(const unsigned char *)"\2  .  .  ;  ;   :  :  ;    :  ;        ;   .         .       ",
191 		0
192 	}
193 };
194 
195 static u_long cvt_meinberg P((unsigned char *, int, struct format *, clocktime_t *, void *));
196 static u_long cvt_mgps     P((unsigned char *, int, struct format *, clocktime_t *, void *));
197 static u_long mbg_input    P((parse_t *, unsigned int, timestamp_t *));
198 static u_long gps_input    P((parse_t *, unsigned int, timestamp_t *));
199 
200 struct msg_buf
201 {
202   unsigned short len;		/* len to fill */
203   unsigned short phase;		/* current input phase */
204 };
205 
206 #define MBG_NONE	0	/* no data input */
207 #define MBG_HEADER	1	/* receiving header */
208 #define MBG_DATA	2	/* receiving data */
209 #define MBG_STRING      3	/* receiving standard data message */
210 
211 clockformat_t clock_meinberg[] =
212 {
213 	{
214 		mbg_input,	/* normal input handling */
215 		cvt_meinberg,	/* Meinberg conversion */
216 		pps_one,	/* easy PPS monitoring */
217 		0,		/* conversion configuration */
218 		"Meinberg Standard", /* Meinberg simple format - beware */
219 		32,				/* string buffer */
220 		0		/* no private data (complete pakets) */
221 	},
222 	{
223 		mbg_input,	/* normal input handling */
224 		cvt_meinberg,	/* Meinberg conversion */
225 		pps_one,	/* easy PPS monitoring */
226 		0,		/* conversion configuration */
227 		"Meinberg Extended", /* Meinberg enhanced format */
228 		32,		/* string buffer */
229 		0		/* no private data (complete pakets) */
230 	},
231 	{
232 		gps_input,	/* no input handling */
233 		cvt_mgps,	/* Meinberg GPS166 conversion */
234 		pps_one,	/* easy PPS monitoring */
235 		(void *)&meinberg_fmt[2], /* conversion configuration */
236 		"Meinberg GPS Extended", /* Meinberg FAU GPS format */
237 		512,		/* string buffer */
238 		sizeof(struct msg_buf)	/* no private data (complete pakets) */
239 	}
240 };
241 
242 /*
243  * cvt_meinberg
244  *
245  * convert simple type format
246  */
247 static u_long
248 cvt_meinberg(
249 	     unsigned char *buffer,
250 	     int            size,
251 	     struct format *unused,
252 	     clocktime_t   *clock_time,
253 	     void          *local
254 	     )
255 {
256 	struct format *format;
257 
258 	/*
259 	 * select automagically correct data format
260 	 */
261 	if (Strok(buffer, meinberg_fmt[0].fixed_string))
262 	{
263 		format = &meinberg_fmt[0];
264 	}
265 	else
266 	{
267 		if (Strok(buffer, meinberg_fmt[1].fixed_string))
268 		{
269 			format = &meinberg_fmt[1];
270 		}
271 		else
272 		{
273 			return CVT_FAIL|CVT_BADFMT;
274 		}
275 	}
276 
277 	/*
278 	 * collect data
279 	 */
280 	if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
281 		 format->field_offsets[O_DAY].length) ||
282 	    Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
283 		 format->field_offsets[O_MONTH].length) ||
284 	    Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
285 		 format->field_offsets[O_YEAR].length) ||
286 	    Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
287 		 format->field_offsets[O_HOUR].length) ||
288 	    Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
289 		 format->field_offsets[O_MIN].length) ||
290 	    Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
291 		 format->field_offsets[O_SEC].length))
292 	{
293 		return CVT_FAIL|CVT_BADFMT;
294 	}
295 	else
296 	{
297 		unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
298 
299 		clock_time->usecond = 0;
300 		clock_time->flags   = PARSEB_S_LEAP;
301 
302 		if (clock_time->second == 60)
303 			clock_time->flags |= PARSEB_LEAPSECOND;
304 
305 		/*
306 		 * in the extended timecode format we have also the
307 		 * indication that the timecode is in UTC
308 		 * for compatibilty reasons we start at the USUAL
309 		 * offset (POWERUP flag) and know that the UTC indication
310 		 * is the character before the powerup flag
311 		 */
312 		if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U'))
313 		{
314 			/*
315 			 * timecode is in UTC
316 			 */
317 			clock_time->utcoffset = 0; /* UTC */
318 			clock_time->flags    |= PARSEB_UTC;
319 		}
320 		else
321 		{
322 			/*
323 			 * only calculate UTC offset if MET/MED is in time code
324 			 * or we have the old time code format, where we do not
325 			 * know whether it is UTC time or MET/MED
326 			 * pray that nobody switches to UTC in the *old* standard time code
327 			 * ROMS !!!! The new ROMS have 'U' at the ZONE field - good.
328 			 */
329 			switch (buffer[format->field_offsets[O_ZONE].offset])
330 			{
331 			case ' ':
332 				clock_time->utcoffset = -1*60*60; /* MET */
333 				break;
334 
335 			case 'S':
336 				clock_time->utcoffset = -2*60*60; /* MED */
337 				break;
338 
339 			case 'U':
340 				/*
341 				 * timecode is in UTC
342 				 */
343 				clock_time->utcoffset = 0;        /* UTC */
344 				clock_time->flags    |= PARSEB_UTC;
345 				break;
346 
347 			default:
348 				return CVT_FAIL|CVT_BADFMT;
349 			}
350 		}
351 
352 		/*
353 		 * gather status flags
354 		 */
355 		if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
356 			clock_time->flags    |= PARSEB_DST;
357 
358 		if (f[0] == '#')
359 			clock_time->flags |= PARSEB_POWERUP;
360 
361 		if (f[1] == '*')
362 			clock_time->flags |= PARSEB_NOSYNC;
363 
364 		if (f[3] == '!')
365 			clock_time->flags |= PARSEB_ANNOUNCE;
366 
367 		/*
368 		 * oncoming leap second
369 		 * 'a' code not confirmed - earth is not
370 		 * expected to speed up
371 		 */
372 		if (f[3] == 'A')
373 			clock_time->flags |= PARSEB_LEAPADD;
374 
375 		if (f[3] == 'a')
376 			clock_time->flags |= PARSEB_LEAPDEL;
377 
378 
379 		if (format->flags & MBG_EXTENDED)
380 		{
381 			clock_time->flags |= PARSEB_S_ANTENNA;
382 
383 			/*
384 			 * DCF77 does not encode the direction -
385 			 * so we take the current default -
386 			 * earth slowing down
387 			 */
388 			clock_time->flags &= ~PARSEB_LEAPDEL;
389 
390 			if (f[4] == 'A')
391 				clock_time->flags |= PARSEB_LEAPADD;
392 
393 			if (f[5] == 'R')
394 				clock_time->flags |= PARSEB_ALTERNATE;
395 		}
396 		return CVT_OK;
397 	}
398 }
399 
400 
401 /*
402  * mbg_input
403  *
404  * grep data from input stream
405  */
406 static u_long
407 mbg_input(
408 	  parse_t      *parseio,
409 	  unsigned int  ch,
410 	  timestamp_t  *tstamp
411 	  )
412 {
413 	unsigned int rtc;
414 
415 	parseprintf(DD_PARSE, ("mbg_input(0x%x, 0x%x, ...)\n", (int)parseio, (int)ch));
416 
417 	switch (ch)
418 	{
419 	case STX:
420 		parseprintf(DD_PARSE, ("mbg_input: STX seen\n"));
421 
422 		parseio->parse_index = 1;
423 		parseio->parse_data[0] = ch;
424 		parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
425 		return PARSE_INP_SKIP;
426 
427 	case ETX:
428 		parseprintf(DD_PARSE, ("mbg_input: ETX seen\n"));
429 		if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP)
430 			return parse_end(parseio);
431 		else
432 			return rtc;
433 
434 	default:
435 		return parse_addchar(parseio, ch);
436 	}
437 }
438 
439 
440 /*
441  * cvt_mgps
442  *
443  * convert Meinberg GPS format
444  */
445 static u_long
446 cvt_mgps(
447 	 unsigned char *buffer,
448 	 int            size,
449 	 struct format *format,
450 	 clocktime_t   *clock_time,
451 	 void          *local
452 	)
453 {
454 	if (!Strok(buffer, format->fixed_string))
455 	{
456 		return cvt_meinberg(buffer, size, format, clock_time, local);
457 	}
458 	else
459 	{
460 		if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
461 			 format->field_offsets[O_DAY].length) ||
462 		    Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
463 			 format->field_offsets[O_MONTH].length) ||
464 		    Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
465 			 format->field_offsets[O_YEAR].length) ||
466 		    Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
467 			 format->field_offsets[O_HOUR].length) ||
468 		    Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
469 			 format->field_offsets[O_MIN].length) ||
470 		    Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
471 			 format->field_offsets[O_SEC].length))
472 		{
473 			return CVT_FAIL|CVT_BADFMT;
474 		}
475 		else
476 		{
477 			long h;
478 			unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
479 
480 			clock_time->flags = PARSEB_S_LEAP|PARSEB_S_POSITION;
481 
482 			clock_time->usecond = 0;
483 
484 			/*
485 			 * calculate UTC offset
486 			 */
487 			if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h,
488 				 format->field_offsets[O_UTCHOFFSET].length))
489 			{
490 				return CVT_FAIL|CVT_BADFMT;
491 			}
492 			else
493 			{
494 				if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock_time->utcoffset,
495 					 format->field_offsets[O_UTCMOFFSET].length))
496 				{
497 					return CVT_FAIL|CVT_BADFMT;
498 				}
499 
500 				clock_time->utcoffset += TIMES60(h);
501 				clock_time->utcoffset  = TIMES60(clock_time->utcoffset);
502 
503 				if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-')
504 				{
505 					clock_time->utcoffset = -clock_time->utcoffset;
506 				}
507 			}
508 
509 			/*
510 			 * gather status flags
511 			 */
512 			if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
513 			    clock_time->flags    |= PARSEB_DST;
514 
515 			if (clock_time->utcoffset == 0)
516 			    clock_time->flags |= PARSEB_UTC;
517 
518 			/*
519 			 * no sv's seen - no time & position
520 			 */
521 			if (f[0] == '#')
522 			    clock_time->flags |= PARSEB_POWERUP;
523 
524 			/*
525 			 * at least one sv seen - time (for last position)
526 			 */
527 			if (f[1] == '*')
528 			    clock_time->flags |= PARSEB_NOSYNC;
529 			else
530 			    if (!(clock_time->flags & PARSEB_POWERUP))
531 				clock_time->flags |= PARSEB_POSITION;
532 
533 			/*
534 			 * oncoming zone switch
535 			 */
536 			if (f[3] == '!')
537 			    clock_time->flags |= PARSEB_ANNOUNCE;
538 
539 			/*
540 			 * oncoming leap second
541 			 * 'a' code not confirmed - earth is not
542 			 * expected to speed up
543 			 */
544 			if (f[4] == 'A')
545 			    clock_time->flags |= PARSEB_LEAPADD;
546 
547 			if (f[4] == 'a')
548 			    clock_time->flags |= PARSEB_LEAPDEL;
549 
550 			/*
551 			 * f[5] == ' '
552 			 */
553 
554 			/*
555 			 * this is the leap second
556 			 */
557 			if ((f[6] == 'L') || (clock_time->second == 60))
558 			    clock_time->flags |= PARSEB_LEAPSECOND;
559 
560 			return CVT_OK;
561 		}
562 	}
563 }
564 
565 /*
566  * gps_input
567  *
568  * grep binary data from input stream
569  */
570 static u_long
571 gps_input(
572 	  parse_t      *parseio,
573 	  unsigned int  ch,
574 	  timestamp_t  *tstamp
575 	  )
576 {
577   CSUM calc_csum;                    /* used to compare the incoming csums */
578   GPS_MSG_HDR header;
579   struct msg_buf *msg_buf;
580 
581   msg_buf = (struct msg_buf *)parseio->parse_pdata;
582 
583   parseprintf(DD_PARSE, ("gps_input(0x%x, 0x%x, ...)\n", (int)parseio, (int)ch));
584 
585   if (!msg_buf)
586     return PARSE_INP_SKIP;
587 
588   if ( msg_buf->phase == MBG_NONE )
589     {                  /* not receiving yet */
590       switch (ch)
591 	{
592 	case SOH:
593 	  parseprintf(DD_PARSE, ("gps_input: SOH seen\n"));
594 
595 	  msg_buf->len = sizeof( header ); /* prepare to receive msg header */
596 	  msg_buf->phase = MBG_HEADER; /* receiving header */
597 	  break;
598 
599 	case STX:
600 	  parseprintf(DD_PARSE, ("gps_input: STX seen\n"));
601 
602 	  msg_buf->len = 0;
603 	  msg_buf->phase = MBG_STRING; /* prepare to receive ASCII ETX delimited message */
604 	  parseio->parse_index = 1;
605 	  parseio->parse_data[0] = ch;
606 	  break;
607 
608 	default:
609 	  return PARSE_INP_SKIP;	/* keep searching */
610 	}
611 
612       parseio->parse_dtime.parse_msglen = 1; /* reset buffer pointer */
613       parseio->parse_dtime.parse_msg[0] = ch; /* fill in first character */
614       parseio->parse_dtime.parse_stime  = *tstamp; /* collect timestamp */
615       return PARSE_INP_SKIP;
616     }
617 
618   /* SOH/STX has already been received */
619 
620   /* save incoming character in both buffers if needbe */
621   if ((msg_buf->phase == MBG_STRING) &&
622       (parseio->parse_index < parseio->parse_dsize))
623     parseio->parse_data[parseio->parse_index++] = ch;
624 
625   parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
626 
627   if (parseio->parse_dtime.parse_msglen > sizeof(parseio->parse_dtime.parse_msg))
628     {
629       msg_buf->phase = MBG_NONE; /* buffer overflow - discard */
630       parseio->parse_data[parseio->parse_index] = '\0';
631       memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
632       parseio->parse_ldsize = parseio->parse_index+1;
633       return PARSE_INP_DATA;
634     }
635 
636   switch (msg_buf->phase)
637     {
638     case MBG_HEADER:
639     case MBG_DATA:
640       msg_buf->len--;
641 
642       if ( msg_buf->len )               /* transfer not complete */
643 	return PARSE_INP_SKIP;
644 
645       parseprintf(DD_PARSE, ("gps_input: %s complete\n", (msg_buf->phase == MBG_DATA) ? "data" : "header"));
646 
647       break;
648 
649     case MBG_STRING:
650       if ((ch == ETX) || (parseio->parse_index >= parseio->parse_dsize))
651 	{
652 	  msg_buf->phase = MBG_NONE;
653 	  parseprintf(DD_PARSE, ("gps_input: string complete\n"));
654 	  parseio->parse_data[parseio->parse_index] = '\0';
655 	  memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
656 	  parseio->parse_ldsize = parseio->parse_index+1;
657 	  parseio->parse_index = 0;
658 	  return PARSE_INP_TIME;
659 	}
660       else
661 	{
662 	  return PARSE_INP_SKIP;
663 	}
664     }
665 
666   /* cnt == 0, so the header or the whole message is complete */
667 
668   if ( msg_buf->phase == MBG_HEADER )
669     {         /* header complete now */
670       unsigned char *datap = parseio->parse_dtime.parse_msg + 1;
671 
672       get_mbg_header(&datap, &header);
673 
674       parseprintf(DD_PARSE, ("gps_input: header: cmd 0x%x, len %d, dcsum 0x%x, hcsum 0x%x\n",
675 			     (int)header.gps_cmd, (int)header.gps_len, (int)header.gps_data_csum,
676 			     (int)header.gps_hdr_csum));
677 
678 
679       calc_csum = mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg + 1, (unsigned short)6 );
680 
681       if ( calc_csum != header.gps_hdr_csum )
682 	{
683 	  parseprintf(DD_PARSE, ("gps_input: header checksum mismatch expected 0x%x, got 0x%x\n",
684 				 (int)calc_csum, (int)mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg, (unsigned short)6 )));
685 
686 	  msg_buf->phase = MBG_NONE;  /* back to hunting mode */
687 	  return PARSE_INP_DATA;      /* invalid header checksum received - pass up for detection */
688 	}
689 
690       if ((header.gps_len == 0)  ||       /* no data to wait for */
691 	  (header.gps_len >= (sizeof (parseio->parse_dtime.parse_msg) - sizeof(header) - 1)))	/* blows anything we have space for */
692 	{
693 	  msg_buf->phase = MBG_NONE;  /* back to hunting mode */
694 	  return (header.gps_len == 0) ? PARSE_INP_DATA : PARSE_INP_SKIP; /* message complete/throwaway */
695 	}
696 
697       parseprintf(DD_PARSE, ("gps_input: expecting %d bytes of data message\n", (int)header.gps_len));
698 
699       msg_buf->len   = header.gps_len;/* save number of bytes to wait for */
700       msg_buf->phase = MBG_DATA;      /* flag header already complete */
701       return PARSE_INP_SKIP;
702     }
703 
704   parseprintf(DD_PARSE, ("gps_input: message data complete\n"));
705 
706   /* Header and data have been received. The header checksum has been */
707   /* checked */
708 
709   msg_buf->phase = MBG_NONE;	      /* back to hunting mode */
710   return PARSE_INP_DATA;              /* message complete, must be evaluated */
711 }
712 
713 #else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_MEINBERG) */
714 int clk_meinberg_bs;
715 #endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_MEINBERG) */
716 
717 /*
718  * History:
719  *
720  * clk_meinberg.c,v
721  * Revision 4.8  1999/11/28 09:13:50  kardel
722  * RECON_4_0_98F
723  *
724  * Revision 4.7  1999/02/21 11:09:14  kardel
725  * cleanup
726  *
727  * Revision 4.6  1998/06/14 21:09:36  kardel
728  * Sun acc cleanup
729  *
730  * Revision 4.5  1998/06/13 15:18:54  kardel
731  * fix mem*() to b*() function macro emulation
732  *
733  * Revision 4.4  1998/06/13 12:03:23  kardel
734  * fix SYSV clock name clash
735  *
736  * Revision 4.3  1998/06/12 15:22:28  kardel
737  * fix prototypes
738  *
739  * Revision 4.2  1998/05/24 16:14:42  kardel
740  * support current Meinberg standard data formats
741  *
742  * Revision 4.1  1998/05/24 09:39:52  kardel
743  * implementation of the new IO handling model
744  *
745  * Revision 4.0  1998/04/10 19:45:29  kardel
746  * Start 4.0 release version numbering
747  *
748  * from V3 3.23 - log info deleted 1998/04/11 kardel
749  *
750  */
751