xref: /freebsd/contrib/ntp/libparse/clk_meinberg.c (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
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 
157 CSUM
158 mbg_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 
173 void
174 get_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 
185 static 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 
217 static parse_cvt_fnc_t cvt_meinberg;
218 static parse_cvt_fnc_t cvt_mgps;
219 static parse_inp_fnc_t mbg_input;
220 static parse_inp_fnc_t gps_input;
221 
222 struct 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 
233 clockformat_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  */
269 static u_long
270 cvt_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  */
428 static u_long
429 mbg_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  */
467 static u_long
468 cvt_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  */
592 static u_long
593 gps_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) */
736 NONEMPTY_TRANSLATION_UNIT
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