154359Sroberto/*
2182007Sroberto * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_meinberg.c,v 4.12.2.1 2005/09/25 10:22:35 kardel RELEASE_20050925_A
3282408Scy *
4182007Sroberto * clk_meinberg.c,v 4.12.2.1 2005/09/25 10:22:35 kardel RELEASE_20050925_A
554359Sroberto *
654359Sroberto * Meinberg clock support
754359Sroberto *
8285169Scy * Copyright (c) 1995-2015 by Frank Kardel <kardel <AT> ntp.org>
9282408Scy * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
1054359Sroberto *
11182007Sroberto * Redistribution and use in source and binary forms, with or without
12182007Sroberto * modification, are permitted provided that the following conditions
13182007Sroberto * are met:
14182007Sroberto * 1. Redistributions of source code must retain the above copyright
15182007Sroberto *    notice, this list of conditions and the following disclaimer.
16182007Sroberto * 2. Redistributions in binary form must reproduce the above copyright
17182007Sroberto *    notice, this list of conditions and the following disclaimer in the
18182007Sroberto *    documentation and/or other materials provided with the distribution.
19182007Sroberto * 3. Neither the name of the author nor the names of its contributors
20182007Sroberto *    may be used to endorse or promote products derived from this software
21182007Sroberto *    without specific prior written permission.
22182007Sroberto *
23182007Sroberto * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24182007Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25182007Sroberto * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26182007Sroberto * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27182007Sroberto * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28182007Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29182007Sroberto * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30182007Sroberto * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31182007Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32182007Sroberto * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33182007Sroberto * SUCH DAMAGE.
34182007Sroberto *
3554359Sroberto */
3654359Sroberto
3754359Sroberto#ifdef HAVE_CONFIG_H
3854359Sroberto# include <config.h>
3954359Sroberto#endif
4054359Sroberto
4154359Sroberto#if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_MEINBERG)
4254359Sroberto
4354359Sroberto#include "ntp_fp.h"
4454359Sroberto#include "ntp_unixtime.h"
4554359Sroberto#include "ntp_calendar.h"
4654359Sroberto
4754359Sroberto#include "ntp_machine.h"
4854359Sroberto
4954359Sroberto#include "parse.h"
5054359Sroberto
5154359Sroberto#ifndef PARSESTREAM
5254359Sroberto#include <stdio.h>
5354359Sroberto#else
5454359Sroberto#include "sys/parsestreams.h"
5554359Sroberto#endif
5654359Sroberto
5754359Sroberto#include "ntp_stdlib.h"
5854359Sroberto
5954359Sroberto#include "ntp_stdlib.h"
6054359Sroberto
6154359Sroberto#include "mbg_gps166.h"
6254359Sroberto#include "binio.h"
6354359Sroberto#include "ascii.h"
6454359Sroberto
6554359Sroberto/*
6654359Sroberto * The Meinberg receiver every second sends a datagram of the following form
6754359Sroberto * (Standard Format)
68282408Scy *
6954359Sroberto *     <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
7054359Sroberto * pos:  0  00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2  2  3  3   3
7154359Sroberto *       1  23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8  9  0  1   2
7254359Sroberto * <STX>           = '\002' ASCII start of text
7354359Sroberto * <ETX>           = '\003' ASCII end of text
7454359Sroberto * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
7554359Sroberto * <w>             = day of week (sunday= 0)
7654359Sroberto * <hh>,<mm>,<ss>  = hour, minute, second
7754359Sroberto * <S>             = '#' if never synced since powerup for DCF C51
7854359Sroberto *                 = '#' if not PZF sychronisation available for PZF 535/509
7954359Sroberto *                 = ' ' if ok
8054359Sroberto * <F>             = '*' if time comes from internal quartz
8154359Sroberto *                 = ' ' if completely synched
8254359Sroberto * <D>             = 'S' if daylight saving time is active
8354359Sroberto *                 = 'U' if time is represented in UTC
8454359Sroberto *                 = ' ' if no special condition exists
8554359Sroberto * <A>             = '!' during the hour preceeding an daylight saving time
8654359Sroberto *                       start/end change
8754359Sroberto *                 = 'A' leap second insert warning
8854359Sroberto *                 = ' ' if no special condition exists
8954359Sroberto *
9054359Sroberto * Extended data format (PZFUERL for PZF type clocks)
9154359Sroberto *
9254359Sroberto *     <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
9354359Sroberto * pos:  0   00 0 00 0 00 11 1 11 11 1 11 2 22 22 2  2  2  2  2  3  3   3
9454359Sroberto *       1   23 4 56 7 89 01 2 34 56 7 89 0 12 34 5  6  7  8  9  0  1   2
9554359Sroberto * <STX>           = '\002' ASCII start of text
9654359Sroberto * <ETX>           = '\003' ASCII end of text
9754359Sroberto * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
9854359Sroberto * <w>             = day of week (sunday= 0)
9954359Sroberto * <hh>,<mm>,<ss>  = hour, minute, second
10054359Sroberto * <U>             = 'U' UTC time display
10154359Sroberto * <S>             = '#' if never synced since powerup else ' ' for DCF C51
10254359Sroberto *                   '#' if not PZF sychronisation available else ' ' for PZF 535/509
10354359Sroberto * <F>             = '*' if time comes from internal quartz else ' '
10454359Sroberto * <D>             = 'S' if daylight saving time is active else ' '
10554359Sroberto * <A>             = '!' during the hour preceeding an daylight saving time
10654359Sroberto *                       start/end change
10754359Sroberto * <L>             = 'A' LEAP second announcement
108282408Scy * <R>             = 'R' "call bit" used to signalize irregularities in the control facilities,
109282408Scy *                   usually ' ', until 2003 indicated transmission via alternate antenna
11054359Sroberto *
111282408Scy * Meinberg GPS receivers
11254359Sroberto *
113282408Scy * For very old devices you must get the Uni-Erlangen firmware for the GPS receiver support
11454359Sroberto * to work to full satisfaction !
115282408Scy * With newer GPS receiver types the Uni Erlangen string format can be configured at the device.
11654359Sroberto *
11754359Sroberto *     <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
11854359Sroberto *
11954359Sroberto *        000000000111111111122222222223333333333444444444455555555556666666
12054359Sroberto *        123456789012345678901234567890123456789012345678901234567890123456
12154359Sroberto *     \x0209.07.93; 5; 08:48:26; +00:00; #*S!A L; 49.5736N  11.0280E  373m\x03
12254359Sroberto *
123282408Scy *
12454359Sroberto * <STX>           = '\002' ASCII start of text
12554359Sroberto * <ETX>           = '\003' ASCII end of text
12654359Sroberto * <dd>,<mm>,<yy>  = day, month, year(2 digits!!)
12754359Sroberto * <w>             = day of week (sunday= 0)
12854359Sroberto * <hh>,<mm>,<ss>  = hour, minute, second
12954359Sroberto * <+/->,<00:00>   = offset to UTC
13054359Sroberto * <S>             = '#' if never synced since powerup else ' '
13154359Sroberto * <F>             = '*' if position is not confirmed else ' '
13254359Sroberto * <D>             = 'S' if daylight saving time is active else ' '
13354359Sroberto * <A>             = '!' during the hour preceeding an daylight saving time
13454359Sroberto *                       start/end change
13554359Sroberto * <L>             = 'A' LEAP second announcement
136282408Scy * <R>             = 'R' "call bit" used to signalize irregularities in the control facilities,
137282408Scy *                   usually ' ', until 2003 indicated transmission via alternate antenna
138282408Scy *                   (reminiscent of PZF receivers)
139282408Scy * <L>             = 'L' on 23:59:60
14054359Sroberto *
14154359Sroberto * Binary messages have a lead in for a fixed header of SOH
14254359Sroberto */
14354359Sroberto
14454359Sroberto/*--------------------------------------------------------------*/
14554359Sroberto/* Name:         csum()                                         */
14654359Sroberto/*                                                              */
14754359Sroberto/* Purpose:      Compute a checksum about a number of bytes     */
14854359Sroberto/*                                                              */
14954359Sroberto/* Input:        uchar *p    address of the first byte          */
15054359Sroberto/*               short n     the number of bytes                */
15154359Sroberto/*                                                              */
15254359Sroberto/* Output:       --                                             */
15354359Sroberto/*                                                              */
15454359Sroberto/* Ret val:      the checksum                                   */
15554359Sroberto/*+-------------------------------------------------------------*/
15654359Sroberto
157282408ScyCSUM
15854359Srobertombg_csum(
15954359Sroberto	 unsigned char *p,
16054359Sroberto	 unsigned int n
16154359Sroberto	 )
16254359Sroberto{
163282408Scy  unsigned int sum = 0;
164280849Scy  unsigned int i;
165282408Scy
16654359Sroberto  for ( i = 0; i < n; i++ )
16754359Sroberto    sum += *p++;
168282408Scy
169282408Scy  return (CSUM) sum;
170282408Scy
17154359Sroberto}  /* csum */
17254359Sroberto
17354359Srobertovoid
17454359Srobertoget_mbg_header(
17554359Sroberto	       unsigned char **bufpp,
17654359Sroberto	       GPS_MSG_HDR *headerp
17754359Sroberto	       )
17854359Sroberto{
179282408Scy  headerp->cmd = (GPS_CMD) get_lsb_short(bufpp);
180282408Scy  headerp->len = get_lsb_uint16(bufpp);
181282408Scy  headerp->data_csum = (CSUM) get_lsb_short(bufpp);
182282408Scy  headerp->hdr_csum  = (CSUM) get_lsb_short(bufpp);
18354359Sroberto}
18454359Sroberto
18554359Srobertostatic struct format meinberg_fmt[] =
18654359Sroberto{
18754359Sroberto	{
18854359Sroberto		{
18954359Sroberto			{ 3, 2},  {  6, 2}, {  9, 2},
19054359Sroberto			{ 18, 2}, { 21, 2}, { 24, 2},
19154359Sroberto			{ 14, 1}, { 27, 4}, { 29, 1},
19254359Sroberto		},
19354359Sroberto		(const unsigned char *)"\2D:  .  .  ;T: ;U:  .  .  ;    \3",
19454359Sroberto		0
19554359Sroberto	},
19654359Sroberto	{			/* special extended FAU Erlangen extended format */
19754359Sroberto		{
19854359Sroberto			{ 1, 2},  { 4,  2}, {  7, 2},
19954359Sroberto			{ 14, 2}, { 17, 2}, { 20, 2},
20054359Sroberto			{ 11, 1}, { 25, 4}, { 27, 1},
20154359Sroberto		},
20254359Sroberto		(const unsigned char *)"\2  .  .  ;  ;   :  :  ;        \3",
20354359Sroberto		MBG_EXTENDED
20454359Sroberto	},
20554359Sroberto	{			/* special extended FAU Erlangen GPS format */
20654359Sroberto		{
20754359Sroberto			{ 1,  2}, {  4, 2}, {  7, 2},
20854359Sroberto			{ 14, 2}, { 17, 2}, { 20, 2},
20954359Sroberto			{ 11, 1}, { 32, 7}, { 35, 1},
21054359Sroberto			{ 25, 2}, { 28, 2}, { 24, 1}
21154359Sroberto		},
21254359Sroberto		(const unsigned char *)"\2  .  .  ;  ;   :  :  ;    :  ;        ;   .         .       ",
21354359Sroberto		0
21454359Sroberto	}
21554359Sroberto};
21654359Sroberto
217282408Scystatic parse_cvt_fnc_t cvt_meinberg;
218282408Scystatic parse_cvt_fnc_t cvt_mgps;
219282408Scystatic parse_inp_fnc_t mbg_input;
220282408Scystatic parse_inp_fnc_t gps_input;
22154359Sroberto
22254359Srobertostruct msg_buf
22354359Sroberto{
22454359Sroberto  unsigned short len;		/* len to fill */
22554359Sroberto  unsigned short phase;		/* current input phase */
22654359Sroberto};
22754359Sroberto
22854359Sroberto#define MBG_NONE	0	/* no data input */
22954359Sroberto#define MBG_HEADER	1	/* receiving header */
23054359Sroberto#define MBG_DATA	2	/* receiving data */
23154359Sroberto#define MBG_STRING      3	/* receiving standard data message */
232282408Scy
23354359Srobertoclockformat_t clock_meinberg[] =
23454359Sroberto{
23554359Sroberto	{
23654359Sroberto		mbg_input,	/* normal input handling */
23754359Sroberto		cvt_meinberg,	/* Meinberg conversion */
23854359Sroberto		pps_one,	/* easy PPS monitoring */
23954359Sroberto		0,		/* conversion configuration */
24054359Sroberto		"Meinberg Standard", /* Meinberg simple format - beware */
24154359Sroberto		32,				/* string buffer */
242282408Scy		0		/* no private data (complete packets) */
24354359Sroberto	},
24454359Sroberto	{
24554359Sroberto		mbg_input,	/* normal input handling */
24654359Sroberto		cvt_meinberg,	/* Meinberg conversion */
24754359Sroberto		pps_one,	/* easy PPS monitoring */
24854359Sroberto		0,		/* conversion configuration */
24954359Sroberto		"Meinberg Extended", /* Meinberg enhanced format */
25054359Sroberto		32,		/* string buffer */
251282408Scy		0		/* no private data (complete packets) */
25254359Sroberto	},
25354359Sroberto	{
25454359Sroberto		gps_input,	/* no input handling */
255282408Scy		cvt_mgps,	/* Meinberg GPS receiver conversion */
25654359Sroberto		pps_one,	/* easy PPS monitoring */
25754359Sroberto		(void *)&meinberg_fmt[2], /* conversion configuration */
258282408Scy		"Meinberg GPS Extended",  /* Meinberg FAU GPS format */
25954359Sroberto		512,		/* string buffer */
260282408Scy		sizeof(struct msg_buf)	/* no private data (complete packets) */
26154359Sroberto	}
26254359Sroberto};
26354359Sroberto
26454359Sroberto/*
265282408Scy * parse_cvt_fnc_t cvt_meinberg
26654359Sroberto *
26754359Sroberto * convert simple type format
26854359Sroberto */
26954359Srobertostatic u_long
27054359Srobertocvt_meinberg(
27154359Sroberto	     unsigned char *buffer,
27254359Sroberto	     int            size,
27354359Sroberto	     struct format *unused,
27454359Sroberto	     clocktime_t   *clock_time,
27554359Sroberto	     void          *local
27654359Sroberto	     )
27754359Sroberto{
27854359Sroberto	struct format *format;
279282408Scy
28054359Sroberto	/*
28154359Sroberto	 * select automagically correct data format
28254359Sroberto	 */
28354359Sroberto	if (Strok(buffer, meinberg_fmt[0].fixed_string))
28454359Sroberto	{
28554359Sroberto		format = &meinberg_fmt[0];
28654359Sroberto	}
28754359Sroberto	else
28854359Sroberto	{
28954359Sroberto		if (Strok(buffer, meinberg_fmt[1].fixed_string))
29054359Sroberto		{
29154359Sroberto			format = &meinberg_fmt[1];
29254359Sroberto		}
29354359Sroberto		else
29454359Sroberto		{
29554359Sroberto			return CVT_FAIL|CVT_BADFMT;
29654359Sroberto		}
29754359Sroberto	}
29854359Sroberto
29954359Sroberto	/*
30054359Sroberto	 * collect data
30154359Sroberto	 */
30254359Sroberto	if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
30354359Sroberto		 format->field_offsets[O_DAY].length) ||
30454359Sroberto	    Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
30554359Sroberto		 format->field_offsets[O_MONTH].length) ||
30654359Sroberto	    Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
30754359Sroberto		 format->field_offsets[O_YEAR].length) ||
30854359Sroberto	    Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
30954359Sroberto		 format->field_offsets[O_HOUR].length) ||
31054359Sroberto	    Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
31154359Sroberto		 format->field_offsets[O_MIN].length) ||
31254359Sroberto	    Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
31354359Sroberto		 format->field_offsets[O_SEC].length))
31454359Sroberto	{
31554359Sroberto		return CVT_FAIL|CVT_BADFMT;
31654359Sroberto	}
31754359Sroberto	else
31854359Sroberto	{
31954359Sroberto		unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
320282408Scy
32154359Sroberto		clock_time->usecond = 0;
32254359Sroberto		clock_time->flags   = PARSEB_S_LEAP;
32354359Sroberto
32454359Sroberto		if (clock_time->second == 60)
32554359Sroberto			clock_time->flags |= PARSEB_LEAPSECOND;
32654359Sroberto
32754359Sroberto		/*
32854359Sroberto		 * in the extended timecode format we have also the
32954359Sroberto		 * indication that the timecode is in UTC
33054359Sroberto		 * for compatibilty reasons we start at the USUAL
33154359Sroberto		 * offset (POWERUP flag) and know that the UTC indication
33254359Sroberto		 * is the character before the powerup flag
33354359Sroberto		 */
33454359Sroberto		if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U'))
33554359Sroberto		{
33654359Sroberto			/*
33754359Sroberto			 * timecode is in UTC
33854359Sroberto			 */
33954359Sroberto			clock_time->utcoffset = 0; /* UTC */
34054359Sroberto			clock_time->flags    |= PARSEB_UTC;
34154359Sroberto		}
34254359Sroberto		else
34354359Sroberto		{
34454359Sroberto			/*
34554359Sroberto			 * only calculate UTC offset if MET/MED is in time code
34654359Sroberto			 * or we have the old time code format, where we do not
34754359Sroberto			 * know whether it is UTC time or MET/MED
34854359Sroberto			 * pray that nobody switches to UTC in the *old* standard time code
34954359Sroberto			 * ROMS !!!! The new ROMS have 'U' at the ZONE field - good.
35054359Sroberto			 */
35154359Sroberto			switch (buffer[format->field_offsets[O_ZONE].offset])
35254359Sroberto			{
35354359Sroberto			case ' ':
35454359Sroberto				clock_time->utcoffset = -1*60*60; /* MET */
35554359Sroberto				break;
356282408Scy
35754359Sroberto			case 'S':
35854359Sroberto				clock_time->utcoffset = -2*60*60; /* MED */
35954359Sroberto				break;
36054359Sroberto
36154359Sroberto			case 'U':
36254359Sroberto				/*
36354359Sroberto				 * timecode is in UTC
36454359Sroberto				 */
36554359Sroberto				clock_time->utcoffset = 0;        /* UTC */
36654359Sroberto				clock_time->flags    |= PARSEB_UTC;
36754359Sroberto				break;
368282408Scy
36954359Sroberto			default:
37054359Sroberto				return CVT_FAIL|CVT_BADFMT;
37154359Sroberto			}
37254359Sroberto		}
373282408Scy
37454359Sroberto		/*
37554359Sroberto		 * gather status flags
37654359Sroberto		 */
37754359Sroberto		if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
37854359Sroberto			clock_time->flags    |= PARSEB_DST;
379282408Scy
38054359Sroberto		if (f[0] == '#')
38154359Sroberto			clock_time->flags |= PARSEB_POWERUP;
382282408Scy
38354359Sroberto		if (f[1] == '*')
38454359Sroberto			clock_time->flags |= PARSEB_NOSYNC;
385282408Scy
38654359Sroberto		if (f[3] == '!')
38754359Sroberto			clock_time->flags |= PARSEB_ANNOUNCE;
388282408Scy
38954359Sroberto		/*
39054359Sroberto		 * oncoming leap second
39154359Sroberto		 * 'a' code not confirmed - earth is not
39254359Sroberto		 * expected to speed up
39354359Sroberto		 */
39454359Sroberto		if (f[3] == 'A')
39554359Sroberto			clock_time->flags |= PARSEB_LEAPADD;
396282408Scy
39754359Sroberto		if (f[3] == 'a')
39854359Sroberto			clock_time->flags |= PARSEB_LEAPDEL;
399282408Scy
400282408Scy
40154359Sroberto		if (format->flags & MBG_EXTENDED)
40254359Sroberto		{
403285169Scy			clock_time->flags |= PARSEB_S_CALLBIT;
404282408Scy
40554359Sroberto			/*
40654359Sroberto			 * DCF77 does not encode the direction -
40754359Sroberto			 * so we take the current default -
40854359Sroberto			 * earth slowing down
40954359Sroberto			 */
41054359Sroberto			clock_time->flags &= ~PARSEB_LEAPDEL;
411282408Scy
41254359Sroberto			if (f[4] == 'A')
41354359Sroberto				clock_time->flags |= PARSEB_LEAPADD;
414282408Scy
41554359Sroberto			if (f[5] == 'R')
416282408Scy				clock_time->flags |= PARSEB_CALLBIT;
41754359Sroberto		}
41854359Sroberto		return CVT_OK;
41954359Sroberto	}
42054359Sroberto}
42154359Sroberto
42254359Sroberto
42354359Sroberto/*
424282408Scy * parse_inp_fnc_t mbg_input
42554359Sroberto *
426282408Scy * grab data from input stream
42754359Sroberto */
42854359Srobertostatic u_long
42954359Srobertombg_input(
43054359Sroberto	  parse_t      *parseio,
431282408Scy	  char         ch,
43254359Sroberto	  timestamp_t  *tstamp
43354359Sroberto	  )
43454359Sroberto{
43554359Sroberto	unsigned int rtc;
436282408Scy
437293423Sdelphij	parseprintf(DD_PARSE, ("mbg_input(0x%p, 0x%x, ...)\n", (void*)parseio, ch));
438282408Scy
43954359Sroberto	switch (ch)
44054359Sroberto	{
44154359Sroberto	case STX:
44254359Sroberto		parseprintf(DD_PARSE, ("mbg_input: STX seen\n"));
443282408Scy
44454359Sroberto		parseio->parse_index = 1;
44554359Sroberto		parseio->parse_data[0] = ch;
44654359Sroberto		parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
44754359Sroberto		return PARSE_INP_SKIP;
448282408Scy
44954359Sroberto	case ETX:
45054359Sroberto		parseprintf(DD_PARSE, ("mbg_input: ETX seen\n"));
45154359Sroberto		if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP)
45254359Sroberto			return parse_end(parseio);
45354359Sroberto		else
45454359Sroberto			return rtc;
45554359Sroberto
45654359Sroberto	default:
45754359Sroberto		return parse_addchar(parseio, ch);
45854359Sroberto	}
45954359Sroberto}
46054359Sroberto
46154359Sroberto
46254359Sroberto/*
463282408Scy * parse_cvt_fnc_t cvt_mgps
46454359Sroberto *
46554359Sroberto * convert Meinberg GPS format
46654359Sroberto */
46754359Srobertostatic u_long
46854359Srobertocvt_mgps(
46954359Sroberto	 unsigned char *buffer,
47054359Sroberto	 int            size,
47154359Sroberto	 struct format *format,
47254359Sroberto	 clocktime_t   *clock_time,
47354359Sroberto	 void          *local
47454359Sroberto	)
47554359Sroberto{
47654359Sroberto	if (!Strok(buffer, format->fixed_string))
47754359Sroberto	{
47854359Sroberto		return cvt_meinberg(buffer, size, format, clock_time, local);
47954359Sroberto	}
48054359Sroberto	else
48154359Sroberto	{
48254359Sroberto		if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
48354359Sroberto			 format->field_offsets[O_DAY].length) ||
48454359Sroberto		    Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
48554359Sroberto			 format->field_offsets[O_MONTH].length) ||
48654359Sroberto		    Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
48754359Sroberto			 format->field_offsets[O_YEAR].length) ||
48854359Sroberto		    Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
48954359Sroberto			 format->field_offsets[O_HOUR].length) ||
49054359Sroberto		    Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
49154359Sroberto			 format->field_offsets[O_MIN].length) ||
49254359Sroberto		    Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
49354359Sroberto			 format->field_offsets[O_SEC].length))
49454359Sroberto		{
49554359Sroberto			return CVT_FAIL|CVT_BADFMT;
49654359Sroberto		}
49754359Sroberto		else
49854359Sroberto		{
49954359Sroberto			long h;
50054359Sroberto			unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
501282408Scy
50254359Sroberto			clock_time->flags = PARSEB_S_LEAP|PARSEB_S_POSITION;
503282408Scy
50454359Sroberto			clock_time->usecond = 0;
50554359Sroberto
50654359Sroberto			/*
50754359Sroberto			 * calculate UTC offset
50854359Sroberto			 */
50954359Sroberto			if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h,
51054359Sroberto				 format->field_offsets[O_UTCHOFFSET].length))
51154359Sroberto			{
51254359Sroberto				return CVT_FAIL|CVT_BADFMT;
51354359Sroberto			}
51454359Sroberto			else
51554359Sroberto			{
51654359Sroberto				if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock_time->utcoffset,
51754359Sroberto					 format->field_offsets[O_UTCMOFFSET].length))
51854359Sroberto				{
51954359Sroberto					return CVT_FAIL|CVT_BADFMT;
52054359Sroberto				}
52154359Sroberto
52254359Sroberto				clock_time->utcoffset += TIMES60(h);
52354359Sroberto				clock_time->utcoffset  = TIMES60(clock_time->utcoffset);
52454359Sroberto
52554359Sroberto				if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-')
52654359Sroberto				{
52754359Sroberto					clock_time->utcoffset = -clock_time->utcoffset;
52854359Sroberto				}
52954359Sroberto			}
530282408Scy
53154359Sroberto			/*
53254359Sroberto			 * gather status flags
53354359Sroberto			 */
53454359Sroberto			if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
53554359Sroberto			    clock_time->flags    |= PARSEB_DST;
536282408Scy
53754359Sroberto			if (clock_time->utcoffset == 0)
53854359Sroberto			    clock_time->flags |= PARSEB_UTC;
539282408Scy
54054359Sroberto			/*
54154359Sroberto			 * no sv's seen - no time & position
54254359Sroberto			 */
54354359Sroberto			if (f[0] == '#')
54454359Sroberto			    clock_time->flags |= PARSEB_POWERUP;
545282408Scy
54654359Sroberto			/*
54754359Sroberto			 * at least one sv seen - time (for last position)
54854359Sroberto			 */
54954359Sroberto			if (f[1] == '*')
55054359Sroberto			    clock_time->flags |= PARSEB_NOSYNC;
55154359Sroberto			else
55254359Sroberto			    if (!(clock_time->flags & PARSEB_POWERUP))
55354359Sroberto				clock_time->flags |= PARSEB_POSITION;
554282408Scy
55554359Sroberto			/*
55654359Sroberto			 * oncoming zone switch
55754359Sroberto			 */
55854359Sroberto			if (f[3] == '!')
55954359Sroberto			    clock_time->flags |= PARSEB_ANNOUNCE;
560282408Scy
56154359Sroberto			/*
56254359Sroberto			 * oncoming leap second
56354359Sroberto			 * 'a' code not confirmed - earth is not
56454359Sroberto			 * expected to speed up
56554359Sroberto			 */
56654359Sroberto			if (f[4] == 'A')
56754359Sroberto			    clock_time->flags |= PARSEB_LEAPADD;
568282408Scy
56954359Sroberto			if (f[4] == 'a')
57054359Sroberto			    clock_time->flags |= PARSEB_LEAPDEL;
57154359Sroberto
57254359Sroberto			/*
57354359Sroberto			 * f[5] == ' '
57454359Sroberto			 */
575282408Scy
57654359Sroberto			/*
57754359Sroberto			 * this is the leap second
57854359Sroberto			 */
57954359Sroberto			if ((f[6] == 'L') || (clock_time->second == 60))
58054359Sroberto			    clock_time->flags |= PARSEB_LEAPSECOND;
58154359Sroberto
58254359Sroberto			return CVT_OK;
58354359Sroberto		}
58454359Sroberto	}
58554359Sroberto}
58654359Sroberto
58754359Sroberto/*
588282408Scy * parse_inp_fnc_t gps_input
58954359Sroberto *
59054359Sroberto * grep binary data from input stream
59154359Sroberto */
59254359Srobertostatic u_long
59354359Srobertogps_input(
59454359Sroberto	  parse_t      *parseio,
595282408Scy	  char ch,
59654359Sroberto	  timestamp_t  *tstamp
59754359Sroberto	  )
59854359Sroberto{
59954359Sroberto  CSUM calc_csum;                    /* used to compare the incoming csums */
60054359Sroberto  GPS_MSG_HDR header;
60154359Sroberto  struct msg_buf *msg_buf;
602282408Scy
60354359Sroberto  msg_buf = (struct msg_buf *)parseio->parse_pdata;
60454359Sroberto
605293423Sdelphij  parseprintf(DD_PARSE, ("gps_input(0x%p, 0x%x, ...)\n", (void*)parseio, ch));
60654359Sroberto
60754359Sroberto  if (!msg_buf)
60854359Sroberto    return PARSE_INP_SKIP;
609282408Scy
61054359Sroberto  if ( msg_buf->phase == MBG_NONE )
61154359Sroberto    {                  /* not receiving yet */
61254359Sroberto      switch (ch)
61354359Sroberto	{
61454359Sroberto	case SOH:
61554359Sroberto	  parseprintf(DD_PARSE, ("gps_input: SOH seen\n"));
616282408Scy
61754359Sroberto	  msg_buf->len = sizeof( header ); /* prepare to receive msg header */
61854359Sroberto	  msg_buf->phase = MBG_HEADER; /* receiving header */
61954359Sroberto	  break;
62054359Sroberto
62154359Sroberto	case STX:
62254359Sroberto	  parseprintf(DD_PARSE, ("gps_input: STX seen\n"));
62354359Sroberto
62454359Sroberto	  msg_buf->len = 0;
62554359Sroberto	  msg_buf->phase = MBG_STRING; /* prepare to receive ASCII ETX delimited message */
62654359Sroberto	  parseio->parse_index = 1;
62754359Sroberto	  parseio->parse_data[0] = ch;
62854359Sroberto	  break;
629282408Scy
63054359Sroberto	default:
63154359Sroberto	  return PARSE_INP_SKIP;	/* keep searching */
63254359Sroberto	}
63354359Sroberto
63454359Sroberto      parseio->parse_dtime.parse_msglen = 1; /* reset buffer pointer */
63554359Sroberto      parseio->parse_dtime.parse_msg[0] = ch; /* fill in first character */
63654359Sroberto      parseio->parse_dtime.parse_stime  = *tstamp; /* collect timestamp */
63754359Sroberto      return PARSE_INP_SKIP;
63854359Sroberto    }
63954359Sroberto
64054359Sroberto  /* SOH/STX has already been received */
64154359Sroberto
64254359Sroberto  /* save incoming character in both buffers if needbe */
64354359Sroberto  if ((msg_buf->phase == MBG_STRING) &&
64454359Sroberto      (parseio->parse_index < parseio->parse_dsize))
64554359Sroberto    parseio->parse_data[parseio->parse_index++] = ch;
646282408Scy
64754359Sroberto  parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
64854359Sroberto
64954359Sroberto  if (parseio->parse_dtime.parse_msglen > sizeof(parseio->parse_dtime.parse_msg))
65054359Sroberto    {
65154359Sroberto      msg_buf->phase = MBG_NONE; /* buffer overflow - discard */
65254359Sroberto      parseio->parse_data[parseio->parse_index] = '\0';
65354359Sroberto      memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
654182007Sroberto      parseio->parse_ldsize = parseio->parse_index;
65554359Sroberto      return PARSE_INP_DATA;
65654359Sroberto    }
657282408Scy
65854359Sroberto  switch (msg_buf->phase)
65954359Sroberto    {
66054359Sroberto    case MBG_HEADER:
66154359Sroberto    case MBG_DATA:
66254359Sroberto      msg_buf->len--;
66354359Sroberto
66454359Sroberto      if ( msg_buf->len )               /* transfer not complete */
66554359Sroberto	return PARSE_INP_SKIP;
66654359Sroberto
66754359Sroberto      parseprintf(DD_PARSE, ("gps_input: %s complete\n", (msg_buf->phase == MBG_DATA) ? "data" : "header"));
66854359Sroberto
66954359Sroberto      break;
67054359Sroberto
67154359Sroberto    case MBG_STRING:
67254359Sroberto      if ((ch == ETX) || (parseio->parse_index >= parseio->parse_dsize))
67354359Sroberto	{
67454359Sroberto	  msg_buf->phase = MBG_NONE;
67554359Sroberto	  parseprintf(DD_PARSE, ("gps_input: string complete\n"));
67654359Sroberto	  parseio->parse_data[parseio->parse_index] = '\0';
67754359Sroberto	  memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
678182007Sroberto	  parseio->parse_ldsize = parseio->parse_index;
67954359Sroberto	  parseio->parse_index = 0;
68054359Sroberto	  return PARSE_INP_TIME;
68154359Sroberto	}
68254359Sroberto      else
68354359Sroberto	{
68454359Sroberto	  return PARSE_INP_SKIP;
68554359Sroberto	}
68654359Sroberto    }
68754359Sroberto
68854359Sroberto  /* cnt == 0, so the header or the whole message is complete */
68954359Sroberto
69054359Sroberto  if ( msg_buf->phase == MBG_HEADER )
69154359Sroberto    {         /* header complete now */
69254359Sroberto      unsigned char *datap = parseio->parse_dtime.parse_msg + 1;
693282408Scy
69454359Sroberto      get_mbg_header(&datap, &header);
695282408Scy
69654359Sroberto      parseprintf(DD_PARSE, ("gps_input: header: cmd 0x%x, len %d, dcsum 0x%x, hcsum 0x%x\n",
697282408Scy			     (int)header.cmd, (int)header.len, (int)header.data_csum,
698282408Scy			     (int)header.hdr_csum));
69954359Sroberto
700282408Scy
70154359Sroberto      calc_csum = mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg + 1, (unsigned short)6 );
70254359Sroberto
703282408Scy      if ( calc_csum != header.hdr_csum )
70454359Sroberto	{
70554359Sroberto	  parseprintf(DD_PARSE, ("gps_input: header checksum mismatch expected 0x%x, got 0x%x\n",
70654359Sroberto				 (int)calc_csum, (int)mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg, (unsigned short)6 )));
707282408Scy
70854359Sroberto	  msg_buf->phase = MBG_NONE;  /* back to hunting mode */
70954359Sroberto	  return PARSE_INP_DATA;      /* invalid header checksum received - pass up for detection */
71054359Sroberto	}
71154359Sroberto
712282408Scy      if ((header.len == 0)  ||       /* no data to wait for */
713282408Scy	  (header.len >= (sizeof (parseio->parse_dtime.parse_msg) - sizeof(header) - 1)))	/* blows anything we have space for */
71454359Sroberto	{
71554359Sroberto	  msg_buf->phase = MBG_NONE;  /* back to hunting mode */
716282408Scy	  return (header.len == 0) ? PARSE_INP_DATA : PARSE_INP_SKIP; /* message complete/throwaway */
71754359Sroberto	}
718282408Scy
719282408Scy      parseprintf(DD_PARSE, ("gps_input: expecting %d bytes of data message\n", (int)header.len));
720282408Scy
721282408Scy      msg_buf->len   = header.len;/* save number of bytes to wait for */
72254359Sroberto      msg_buf->phase = MBG_DATA;      /* flag header already complete */
72354359Sroberto      return PARSE_INP_SKIP;
72454359Sroberto    }
72554359Sroberto
72654359Sroberto  parseprintf(DD_PARSE, ("gps_input: message data complete\n"));
727282408Scy
72854359Sroberto  /* Header and data have been received. The header checksum has been */
72954359Sroberto  /* checked */
73054359Sroberto
73154359Sroberto  msg_buf->phase = MBG_NONE;	      /* back to hunting mode */
73254359Sroberto  return PARSE_INP_DATA;              /* message complete, must be evaluated */
73354359Sroberto}
73454359Sroberto
73554359Sroberto#else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_MEINBERG) */
73654359Srobertoint clk_meinberg_bs;
73754359Sroberto#endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_MEINBERG) */
73854359Sroberto
73954359Sroberto/*
74054359Sroberto * History:
74154359Sroberto *
74254359Sroberto * clk_meinberg.c,v
743182007Sroberto * Revision 4.12.2.1  2005/09/25 10:22:35  kardel
744182007Sroberto * cleanup buffer bounds
745182007Sroberto *
746182007Sroberto * Revision 4.12  2005/04/16 17:32:10  kardel
747182007Sroberto * update copyright
748182007Sroberto *
749182007Sroberto * Revision 4.11  2004/11/14 15:29:41  kardel
750182007Sroberto * support PPSAPI, upgrade Copyright to Berkeley style
751182007Sroberto *
75256746Sroberto * Revision 4.8  1999/11/28 09:13:50  kardel
75356746Sroberto * RECON_4_0_98F
75456746Sroberto *
75554359Sroberto * Revision 4.7  1999/02/21 11:09:14  kardel
75654359Sroberto * cleanup
75754359Sroberto *
75854359Sroberto * Revision 4.6  1998/06/14 21:09:36  kardel
75954359Sroberto * Sun acc cleanup
76054359Sroberto *
76154359Sroberto * Revision 4.5  1998/06/13 15:18:54  kardel
76254359Sroberto * fix mem*() to b*() function macro emulation
76354359Sroberto *
76454359Sroberto * Revision 4.4  1998/06/13 12:03:23  kardel
76554359Sroberto * fix SYSV clock name clash
76654359Sroberto *
76754359Sroberto * Revision 4.3  1998/06/12 15:22:28  kardel
76854359Sroberto * fix prototypes
76954359Sroberto *
77054359Sroberto * Revision 4.2  1998/05/24 16:14:42  kardel
77154359Sroberto * support current Meinberg standard data formats
77254359Sroberto *
77354359Sroberto * Revision 4.1  1998/05/24 09:39:52  kardel
77454359Sroberto * implementation of the new IO handling model
77554359Sroberto *
77654359Sroberto * Revision 4.0  1998/04/10 19:45:29  kardel
77754359Sroberto * Start 4.0 release version numbering
77854359Sroberto *
77954359Sroberto * from V3 3.23 - log info deleted 1998/04/11 kardel
78054359Sroberto *
78154359Sroberto */
782