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