#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_MEINBERG)
#include "ntp_fp.h"
#include "ntp_unixtime.h"
#include "ntp_calendar.h"
#include "ntp_machine.h"
#include "parse.h"
#ifndef PARSESTREAM
#include <stdio.h>
#else
#include "sys/parsestreams.h"
#endif
#include "ntp_stdlib.h"
#include "ntp_stdlib.h"
#include "mbg_gps166.h"
#include "binio.h"
#include "ascii.h"
unsigned long
mbg_csum(
unsigned char *p,
unsigned int n
)
{
unsigned long sum = 0;
short i;
for ( i = 0; i < n; i++ )
sum += *p++;
return( sum );
}
void
get_mbg_header(
unsigned char **bufpp,
GPS_MSG_HDR *headerp
)
{
headerp->gps_cmd = get_lsb_short(bufpp);
headerp->gps_len = get_lsb_short(bufpp);
headerp->gps_data_csum = get_lsb_short(bufpp);
headerp->gps_hdr_csum = get_lsb_short(bufpp);
}
static struct format meinberg_fmt[] =
{
{
{
{ 3, 2}, { 6, 2}, { 9, 2},
{ 18, 2}, { 21, 2}, { 24, 2},
{ 14, 1}, { 27, 4}, { 29, 1},
},
(const unsigned char *)"\2D: . . ;T: ;U: . . ; \3",
0
},
{
{
{ 1, 2}, { 4, 2}, { 7, 2},
{ 14, 2}, { 17, 2}, { 20, 2},
{ 11, 1}, { 25, 4}, { 27, 1},
},
(const unsigned char *)"\2 . . ; ; : : ; \3",
MBG_EXTENDED
},
{
{
{ 1, 2}, { 4, 2}, { 7, 2},
{ 14, 2}, { 17, 2}, { 20, 2},
{ 11, 1}, { 32, 7}, { 35, 1},
{ 25, 2}, { 28, 2}, { 24, 1}
},
(const unsigned char *)"\2 . . ; ; : : ; : ; ; . . ",
0
}
};
static u_long cvt_meinberg P((unsigned char *, int, struct format *, clocktime_t *, void *));
static u_long cvt_mgps P((unsigned char *, int, struct format *, clocktime_t *, void *));
static u_long mbg_input P((parse_t *, unsigned int, timestamp_t *));
static u_long gps_input P((parse_t *, unsigned int, timestamp_t *));
struct msg_buf
{
unsigned short len;
unsigned short phase;
};
#define MBG_NONE 0
#define MBG_HEADER 1
#define MBG_DATA 2
#define MBG_STRING 3
clockformat_t clock_meinberg[] =
{
{
mbg_input,
cvt_meinberg,
pps_one,
0,
"Meinberg Standard",
32,
0
},
{
mbg_input,
cvt_meinberg,
pps_one,
0,
"Meinberg Extended",
32,
0
},
{
gps_input,
cvt_mgps,
pps_one,
(void *)&meinberg_fmt[2],
"Meinberg GPS Extended",
512,
sizeof(struct msg_buf)
}
};
static u_long
cvt_meinberg(
unsigned char *buffer,
int size,
struct format *unused,
clocktime_t *clock_time,
void *local
)
{
struct format *format;
if (Strok(buffer, meinberg_fmt[0].fixed_string))
{
format = &meinberg_fmt[0];
}
else
{
if (Strok(buffer, meinberg_fmt[1].fixed_string))
{
format = &meinberg_fmt[1];
}
else
{
return CVT_FAIL|CVT_BADFMT;
}
}
if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
format->field_offsets[O_DAY].length) ||
Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
format->field_offsets[O_MONTH].length) ||
Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
format->field_offsets[O_YEAR].length) ||
Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
format->field_offsets[O_HOUR].length) ||
Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
format->field_offsets[O_MIN].length) ||
Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
format->field_offsets[O_SEC].length))
{
return CVT_FAIL|CVT_BADFMT;
}
else
{
unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
clock_time->usecond = 0;
clock_time->flags = PARSEB_S_LEAP;
if (clock_time->second == 60)
clock_time->flags |= PARSEB_LEAPSECOND;
if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U'))
{
clock_time->utcoffset = 0;
clock_time->flags |= PARSEB_UTC;
}
else
{
switch (buffer[format->field_offsets[O_ZONE].offset])
{
case ' ':
clock_time->utcoffset = -1*60*60;
break;
case 'S':
clock_time->utcoffset = -2*60*60;
break;
case 'U':
clock_time->utcoffset = 0;
clock_time->flags |= PARSEB_UTC;
break;
default:
return CVT_FAIL|CVT_BADFMT;
}
}
if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
clock_time->flags |= PARSEB_DST;
if (f[0] == '#')
clock_time->flags |= PARSEB_POWERUP;
if (f[1] == '*')
clock_time->flags |= PARSEB_NOSYNC;
if (f[3] == '!')
clock_time->flags |= PARSEB_ANNOUNCE;
if (f[3] == 'A')
clock_time->flags |= PARSEB_LEAPADD;
if (f[3] == 'a')
clock_time->flags |= PARSEB_LEAPDEL;
if (format->flags & MBG_EXTENDED)
{
clock_time->flags |= PARSEB_S_ANTENNA;
clock_time->flags &= ~PARSEB_LEAPDEL;
if (f[4] == 'A')
clock_time->flags |= PARSEB_LEAPADD;
if (f[5] == 'R')
clock_time->flags |= PARSEB_ALTERNATE;
}
return CVT_OK;
}
}
static u_long
mbg_input(
parse_t *parseio,
unsigned int ch,
timestamp_t *tstamp
)
{
unsigned int rtc;
parseprintf(DD_PARSE, ("mbg_input(0x%lx, 0x%x, ...)\n", (long)parseio, ch));
switch (ch)
{
case STX:
parseprintf(DD_PARSE, ("mbg_input: STX seen\n"));
parseio->parse_index = 1;
parseio->parse_data[0] = ch;
parseio->parse_dtime.parse_stime = *tstamp;
return PARSE_INP_SKIP;
case ETX:
parseprintf(DD_PARSE, ("mbg_input: ETX seen\n"));
if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP)
return parse_end(parseio);
else
return rtc;
default:
return parse_addchar(parseio, ch);
}
}
static u_long
cvt_mgps(
unsigned char *buffer,
int size,
struct format *format,
clocktime_t *clock_time,
void *local
)
{
if (!Strok(buffer, format->fixed_string))
{
return cvt_meinberg(buffer, size, format, clock_time, local);
}
else
{
if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock_time->day,
format->field_offsets[O_DAY].length) ||
Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock_time->month,
format->field_offsets[O_MONTH].length) ||
Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock_time->year,
format->field_offsets[O_YEAR].length) ||
Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock_time->hour,
format->field_offsets[O_HOUR].length) ||
Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock_time->minute,
format->field_offsets[O_MIN].length) ||
Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock_time->second,
format->field_offsets[O_SEC].length))
{
return CVT_FAIL|CVT_BADFMT;
}
else
{
long h;
unsigned char *f = &buffer[format->field_offsets[O_FLAGS].offset];
clock_time->flags = PARSEB_S_LEAP|PARSEB_S_POSITION;
clock_time->usecond = 0;
if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h,
format->field_offsets[O_UTCHOFFSET].length))
{
return CVT_FAIL|CVT_BADFMT;
}
else
{
if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock_time->utcoffset,
format->field_offsets[O_UTCMOFFSET].length))
{
return CVT_FAIL|CVT_BADFMT;
}
clock_time->utcoffset += TIMES60(h);
clock_time->utcoffset = TIMES60(clock_time->utcoffset);
if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-')
{
clock_time->utcoffset = -clock_time->utcoffset;
}
}
if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
clock_time->flags |= PARSEB_DST;
if (clock_time->utcoffset == 0)
clock_time->flags |= PARSEB_UTC;
if (f[0] == '#')
clock_time->flags |= PARSEB_POWERUP;
if (f[1] == '*')
clock_time->flags |= PARSEB_NOSYNC;
else
if (!(clock_time->flags & PARSEB_POWERUP))
clock_time->flags |= PARSEB_POSITION;
if (f[3] == '!')
clock_time->flags |= PARSEB_ANNOUNCE;
if (f[4] == 'A')
clock_time->flags |= PARSEB_LEAPADD;
if (f[4] == 'a')
clock_time->flags |= PARSEB_LEAPDEL;
if ((f[6] == 'L') || (clock_time->second == 60))
clock_time->flags |= PARSEB_LEAPSECOND;
return CVT_OK;
}
}
}
static u_long
gps_input(
parse_t *parseio,
unsigned int ch,
timestamp_t *tstamp
)
{
CSUM calc_csum;
GPS_MSG_HDR header;
struct msg_buf *msg_buf;
msg_buf = (struct msg_buf *)parseio->parse_pdata;
parseprintf(DD_PARSE, ("gps_input(0x%lx, 0x%x, ...)\n", (long)parseio, ch));
if (!msg_buf)
return PARSE_INP_SKIP;
if ( msg_buf->phase == MBG_NONE )
{
switch (ch)
{
case SOH:
parseprintf(DD_PARSE, ("gps_input: SOH seen\n"));
msg_buf->len = sizeof( header );
msg_buf->phase = MBG_HEADER;
break;
case STX:
parseprintf(DD_PARSE, ("gps_input: STX seen\n"));
msg_buf->len = 0;
msg_buf->phase = MBG_STRING;
parseio->parse_index = 1;
parseio->parse_data[0] = ch;
break;
default:
return PARSE_INP_SKIP;
}
parseio->parse_dtime.parse_msglen = 1;
parseio->parse_dtime.parse_msg[0] = ch;
parseio->parse_dtime.parse_stime = *tstamp;
return PARSE_INP_SKIP;
}
if ((msg_buf->phase == MBG_STRING) &&
(parseio->parse_index < parseio->parse_dsize))
parseio->parse_data[parseio->parse_index++] = ch;
parseio->parse_dtime.parse_msg[parseio->parse_dtime.parse_msglen++] = ch;
if (parseio->parse_dtime.parse_msglen > sizeof(parseio->parse_dtime.parse_msg))
{
msg_buf->phase = MBG_NONE;
parseio->parse_data[parseio->parse_index] = '\0';
memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
parseio->parse_ldsize = parseio->parse_index;
return PARSE_INP_DATA;
}
switch (msg_buf->phase)
{
case MBG_HEADER:
case MBG_DATA:
msg_buf->len--;
if ( msg_buf->len )
return PARSE_INP_SKIP;
parseprintf(DD_PARSE, ("gps_input: %s complete\n", (msg_buf->phase == MBG_DATA) ? "data" : "header"));
break;
case MBG_STRING:
if ((ch == ETX) || (parseio->parse_index >= parseio->parse_dsize))
{
msg_buf->phase = MBG_NONE;
parseprintf(DD_PARSE, ("gps_input: string complete\n"));
parseio->parse_data[parseio->parse_index] = '\0';
memcpy(parseio->parse_ldata, parseio->parse_data, (unsigned)(parseio->parse_index+1));
parseio->parse_ldsize = parseio->parse_index;
parseio->parse_index = 0;
return PARSE_INP_TIME;
}
else
{
return PARSE_INP_SKIP;
}
}
if ( msg_buf->phase == MBG_HEADER )
{
unsigned char *datap = parseio->parse_dtime.parse_msg + 1;
get_mbg_header(&datap, &header);
parseprintf(DD_PARSE, ("gps_input: header: cmd 0x%x, len %d, dcsum 0x%x, hcsum 0x%x\n",
(int)header.gps_cmd, (int)header.gps_len, (int)header.gps_data_csum,
(int)header.gps_hdr_csum));
calc_csum = mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg + 1, (unsigned short)6 );
if ( calc_csum != header.gps_hdr_csum )
{
parseprintf(DD_PARSE, ("gps_input: header checksum mismatch expected 0x%x, got 0x%x\n",
(int)calc_csum, (int)mbg_csum( (unsigned char *) parseio->parse_dtime.parse_msg, (unsigned short)6 )));
msg_buf->phase = MBG_NONE;
return PARSE_INP_DATA;
}
if ((header.gps_len == 0) ||
(header.gps_len >= (sizeof (parseio->parse_dtime.parse_msg) - sizeof(header) - 1)))
{
msg_buf->phase = MBG_NONE;
return (header.gps_len == 0) ? PARSE_INP_DATA : PARSE_INP_SKIP;
}
parseprintf(DD_PARSE, ("gps_input: expecting %d bytes of data message\n", (int)header.gps_len));
msg_buf->len = header.gps_len;
msg_buf->phase = MBG_DATA;
return PARSE_INP_SKIP;
}
parseprintf(DD_PARSE, ("gps_input: message data complete\n"));
msg_buf->phase = MBG_NONE;
return PARSE_INP_DATA;
}
#else
int clk_meinberg_bs;
#endif