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