clk_meinberg.c revision 56746
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
136unsigned long
137mbg_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
151void
152get_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
163static 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
195static u_long cvt_meinberg P((unsigned char *, int, struct format *, clocktime_t *, void *));
196static u_long cvt_mgps     P((unsigned char *, int, struct format *, clocktime_t *, void *));
197static u_long mbg_input    P((parse_t *, unsigned int, timestamp_t *));
198static u_long gps_input    P((parse_t *, unsigned int, timestamp_t *));
199
200struct 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
211clockformat_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 */
247static u_long
248cvt_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 */
406static u_long
407mbg_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 */
445static u_long
446cvt_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 */
570static u_long
571gps_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) */
714int 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