Home | History | Annotate | Line # | Download | only in libparse
      1 /*	$NetBSD: clk_meinberg.c,v 1.8 2024/08/18 20:47:17 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 NONEMPTY_TRANSLATION_UNIT
    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