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