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