xref: /freebsd/contrib/ntp/libparse/clk_meinberg.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
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 
153 unsigned long
154 mbg_csum(
155 	 unsigned char *p,
156 	 unsigned int n
157 	 )
158 {
159   unsigned long sum = 0;
160   short i;
161 
162   for ( i = 0; i < n; i++ )
163     sum += *p++;
164 
165   return( sum );
166 }  /* csum */
167 
168 void
169 get_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 
180 static 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 
212 static u_long cvt_meinberg P((unsigned char *, int, struct format *, clocktime_t *, void *));
213 static u_long cvt_mgps     P((unsigned char *, int, struct format *, clocktime_t *, void *));
214 static u_long mbg_input    P((parse_t *, unsigned int, timestamp_t *));
215 static u_long gps_input    P((parse_t *, unsigned int, timestamp_t *));
216 
217 struct 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 
228 clockformat_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  */
264 static u_long
265 cvt_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  */
423 static u_long
424 mbg_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  */
462 static u_long
463 cvt_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  */
587 static u_long
588 gps_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) */
731 int 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