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