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