/* $NetBSD: clk_rawdcf.c,v 1.4 1998/04/09 17:17:28 thorpej Exp $ */ /* * /src/NTP/REPOSITORY/v4/libparse/clk_rawdcf.c,v 3.23 1997/01/19 12:44:39 kardel Exp * * clk_rawdcf.c,v 3.23 1997/01/19 12:44:39 kardel Exp * * Raw DCF77 pulse clock support * * Copyright (C) 1992,1993,1994,1995,1996 by Frank Kardel * Friedrich-Alexander Universität Erlangen-Nürnberg, Germany * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * */ #ifdef HAVE_CONFIG_H #include #endif #if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_RAWDCF) #include #include #include #include "ntp_fp.h" #include "ntp_unixtime.h" #include "ntp_calendar.h" #include "parse.h" #ifdef PARSESTREAM #include #endif #ifndef PARSEKERNEL #include "ntp_stdlib.h" #endif /* * DCF77 raw time code * * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig * und Berlin, Maerz 1989 * * Timecode transmission: * AM: * time marks are send every second except for the second before the * next minute mark * time marks consist of a reduction of transmitter power to 25% * of the nominal level * the falling edge is the time indication (on time) * time marks of a 100ms duration constitute a logical 0 * time marks of a 200ms duration constitute a logical 1 * FM: * see the spec. (basically a (non-)inverted psuedo random phase shift) * * Encoding: * Second Contents * 0 - 10 AM: free, FM: 0 * 11 - 14 free * 15 R - alternate antenna * 16 A1 - expect zone change (1 hour before) * 17 - 18 Z1,Z2 - time zone * 0 0 illegal * 0 1 MEZ (MET) * 1 0 MESZ (MED, MET DST) * 1 1 illegal * 19 A2 - expect leap insertion/deletion (1 hour before) * 20 S - start of time code (1) * 21 - 24 M1 - BCD (lsb first) Minutes * 25 - 27 M10 - BCD (lsb first) 10 Minutes * 28 P1 - Minute Parity (even) * 29 - 32 H1 - BCD (lsb first) Hours * 33 - 34 H10 - BCD (lsb first) 10 Hours * 35 P2 - Hour Parity (even) * 36 - 39 D1 - BCD (lsb first) Days * 40 - 41 D10 - BCD (lsb first) 10 Days * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) * 45 - 49 MO - BCD (lsb first) Month * 50 MO0 - 10 Months * 51 - 53 Y1 - BCD (lsb first) Years * 54 - 57 Y10 - BCD (lsb first) 10 Years * 58 P3 - Date Parity (even) * 59 - usually missing (minute indication), except for leap insertion */ /* clk_rawdcf.c */ static u_long ext_bf P((char *, int, char *)); static unsigned pcheck P((char *, int, char *)); struct dcfparam; static u_long convert_rawdcf P((unsigned char *, int, struct dcfparam *, clocktime_t *)); static u_long cvt_rawdcf P((char *, unsigned int, void *, clocktime_t *, void *)); static u_long pps_rawdcf P((parse_t *, int, timestamp_t *)); static u_long snt_rawdcf P((parse_t *, timestamp_t *)); clockformat_t clock_rawdcf = { NULL, /* no input handling */ cvt_rawdcf, /* raw dcf input conversion */ NULL, /* no character bound synchronisation */ pps_rawdcf, /* examining PPS information */ snt_rawdcf, /* synthesize time code from input */ (void *)0, /* buffer bit representation */ "RAW DCF77 Timecode", /* direct decoding / time synthesis */ 61, /* bit buffer */ SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE|CVT_FIXEDONLY, /* catch all transitions, buffer restart on timeout, fixed configuration only */ 0, /* no private data (currently in input buffer) */ { 1, 500000}, /* restart after 1.5 seconds */ '\0', '\0', '\0' }; static struct dcfparam { unsigned char onebits[60]; unsigned char zerobits[60]; } dcfparam = { "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */ "--------------------s-------p------p----------------------p" /* 'ZERO' representation */ }; static struct rawdcfcode { char offset; /* start bit */ } rawdcfcode[] = { { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } }; #define DCF_M 0 #define DCF_R 1 #define DCF_A1 2 #define DCF_Z 3 #define DCF_A2 4 #define DCF_S 5 #define DCF_M1 6 #define DCF_M10 7 #define DCF_P1 8 #define DCF_H1 9 #define DCF_H10 10 #define DCF_P2 11 #define DCF_D1 12 #define DCF_D10 13 #define DCF_DW 14 #define DCF_MO 15 #define DCF_MO0 16 #define DCF_Y1 17 #define DCF_Y10 18 #define DCF_P3 19 static struct partab { char offset; /* start bit of parity field */ } partab[] = { { 21 }, { 29 }, { 36 }, { 59 } }; #define DCF_P_P1 0 #define DCF_P_P2 1 #define DCF_P_P3 2 #define DCF_Z_MET 0x2 #define DCF_Z_MED 0x1 static u_long ext_bf(buf, idx, zero) register char *buf; register int idx; register char *zero; { register u_long sum = 0; register int i, first; first = rawdcfcode[idx].offset; for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) { sum <<= 1; sum |= (buf[i] != zero[i]); } return sum; } static unsigned pcheck(buf, idx, zero) register char *buf; register int idx; register char *zero; { register int i,last; register unsigned psum = 1; last = partab[idx+1].offset; for (i = partab[idx].offset; i < last; i++) psum ^= (buf[i] != zero[i]); return psum; } static u_long convert_rawdcf(buffer, size, dcfparam, clock) register unsigned char *buffer; register int size; register struct dcfparam *dcfparam; register clocktime_t *clock; { register unsigned char *s = buffer; register unsigned char *b = dcfparam->onebits; register unsigned char *c = dcfparam->zerobits; register int i; parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%s\"\n", buffer)); if (size < 57) { #ifdef PARSEKERNEL printf("parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size); #else msyslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size); #endif return CVT_NONE; } for (i = 0; i < 58; i++) { if ((*s != *b) && (*s != *c)) { /* * we only have two types of bytes (ones and zeros) */ #ifdef PARSEKERNEL printf("parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer); #else msyslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer); #endif return CVT_NONE; } b++; c++; s++; } /* * check Start and Parity bits */ if ((ext_bf(buffer, DCF_S, dcfparam->zerobits) == 1) && pcheck(buffer, DCF_P_P1, dcfparam->zerobits) && pcheck(buffer, DCF_P_P2, dcfparam->zerobits) && pcheck(buffer, DCF_P_P3, dcfparam->zerobits)) { /* * buffer OK */ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n")); clock->flags = PARSEB_S_ANTENNA|PARSEB_S_LEAP; clock->utctime= 0; clock->usecond= 0; clock->second = 0; clock->minute = ext_bf(buffer, DCF_M10, dcfparam->zerobits); clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1, dcfparam->zerobits); clock->hour = ext_bf(buffer, DCF_H10, dcfparam->zerobits); clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1, dcfparam->zerobits); clock->day = ext_bf(buffer, DCF_D10, dcfparam->zerobits); clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1, dcfparam->zerobits); clock->month = ext_bf(buffer, DCF_MO0, dcfparam->zerobits); clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO, dcfparam->zerobits); clock->year = ext_bf(buffer, DCF_Y10, dcfparam->zerobits); clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1, dcfparam->zerobits); switch (ext_bf(buffer, DCF_Z, dcfparam->zerobits)) { case DCF_Z_MET: clock->utcoffset = -1*60*60; break; case DCF_Z_MED: clock->flags |= PARSEB_DST; clock->utcoffset = -2*60*60; break; default: parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n")); return CVT_FAIL|CVT_BADFMT; } if (ext_bf(buffer, DCF_A1, dcfparam->zerobits)) clock->flags |= PARSEB_ANNOUNCE; if (ext_bf(buffer, DCF_A2, dcfparam->zerobits)) clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ if (ext_bf(buffer, DCF_R, dcfparam->zerobits)) clock->flags |= PARSEB_ALTERNATE; parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%lx\n", (int)clock->hour, (int)clock->minute, (int)clock->day, (int)clock->month,(int) clock->year, (u_long)clock->flags)); return CVT_OK; } else { /* * bad format - not for us */ #ifdef PARSEKERNEL printf("parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer); #else msyslog(LOG_ERR, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer); #endif return CVT_FAIL|CVT_BADFMT; } } /* * raw dcf input routine - needs to fix up 50 baud * characters for 1/0 decision */ static u_long cvt_rawdcf(bp, size, vf, clock, vt) register char *bp; register unsigned int size; register void *vf; register clocktime_t *clock; register void *vt; { register unsigned char *buffer = (unsigned char *) bp; register unsigned char *s = buffer; register unsigned char *e = buffer + size; register unsigned char *b = dcfparam.onebits; register unsigned char *c = dcfparam.zerobits; register unsigned rtc = CVT_NONE; register unsigned int i, lowmax, highmax, cutoff, span; #define BITS 9 unsigned char histbuf[BITS]; /* * the input buffer contains characters with runs of consecutive * bits set. These set bits are an indication of the DCF77 pulse * length. We assume that we receive the pulse at 50 Baud. Thus * a 100ms pulse would generate a 4 bit train (20ms per bit and * start bit) * a 200ms pulse would create all zeroes (and probably a frame error) */ for (i = 0; i < BITS; i++) { histbuf[i] = 0; } cutoff = 0; lowmax = 0; while (s < e) { register unsigned int ch = *s ^ 0xFF; /* * these lines are left as an excercise to the reader 8-) */ if (!((ch+1) & ch) || !*s) { for (i = 0; ch; i++) { ch >>= 1; } *s = i; histbuf[i]++; cutoff += i; lowmax++; } else { parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%ld FAILED\n", *s, (long)(s - buffer))); *s = ~0; rtc = CVT_FAIL|CVT_BADFMT; } s++; } if (lowmax) { cutoff /= lowmax; } else { cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */ } parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff)); lowmax = 0; highmax = 0; parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:")); for (i = 0; i <= cutoff; i++) { lowmax+=histbuf[i] * i; highmax += histbuf[i]; parseprintf(DD_RAWDCF,(" %d", histbuf[i])); } parseprintf(DD_RAWDCF, (" ")); lowmax += highmax / 2; if (highmax) { lowmax /= highmax; } else { lowmax = 0; } highmax = 0; cutoff = 0; for (; i < BITS; i++) { highmax+=histbuf[i] * i; cutoff +=histbuf[i]; parseprintf(DD_RAWDCF,(" %d", histbuf[i])); } parseprintf(DD_RAWDCF,("\n")); if (cutoff) { highmax /= cutoff; } else { highmax = BITS-1; } span = cutoff = lowmax; for (i = lowmax; i <= highmax; i++) { if (histbuf[cutoff] > histbuf[i]) { cutoff = i; span = i; } else if (histbuf[cutoff] == histbuf[i]) { span = i; } } cutoff = (cutoff + span) / 2; parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff)); s = buffer; while ((s < e) && *c && *b) { if (*s == (unsigned char)~0) { *s = '?'; } else { *s = (*s >= cutoff) ? *b : *c; } s++; b++; c++; } return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, &dcfparam, clock) : rtc; } /* * pps_rawdcf * * currently a very stupid version - should be extended to decode * also ones and zeros (which is easy) */ /*ARGSUSED*/ static u_long pps_rawdcf(parseio, status, ptime) register parse_t *parseio; register int status; register timestamp_t *ptime; { if (status) { parseio->parse_dtime.parse_ptime = *ptime; parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; } return CVT_NONE; } /*ARGSUSED*/ static u_long snt_rawdcf(parseio, ptime) register parse_t *parseio; register timestamp_t *ptime; { clocktime_t clock; u_long cvtrtc; time_t t; /* * start at last sample and add second index - gross, may have to be much more careful */ if (convert_rawdcf(parseio->parse_ldata, parseio->parse_ldsize - 1, &dcfparam, &clock) == CVT_OK) { if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1) { parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time conversion FAILED\n")); return CVT_FAIL|cvtrtc; } } else { parseprintf(DD_RAWDCF,("parse: snt_rawdcf: data conversion FAILED\n")); return CVT_NONE; } parseio->parse_dtime.parse_stime = *ptime; t += parseio->parse_index - 1; /* * time stamp */ #ifdef PARSEKERNEL parseio->parse_dtime.parse_time.tv.tv_sec = t; parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond; #else parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970; TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf); #endif parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1)); return updatetimeinfo(parseio, t, clock.usecond, clock.flags); } #else /* not (REFCLOCK && (PARSE || PARSEPPS) && CLOCK_RAWDCF) */ int clk_rawdcf_bs; #endif /* not (REFCLOCK && (PARSE || PARSEPPS) && CLOCK_RAWDCF) */ /* * History: * * clk_rawdcf.c,v * Revision 3.23 1997/01/19 12:44:39 kardel * 3-5.88.1 reconcilation * * Revision 3.22 1996/12/01 16:04:15 kardel * freeze for 5.86.12.2 PARSE-Patch * * Revision 3.21 1996/11/24 23:16:31 kardel * checkpoint - partial autoconfigure update for parse modules * * Revision 3.20 1996/11/24 20:09:45 kardel * RELEASE_5_86_12_2 reconcilation * * Revision 3.19 1996/10/05 13:30:20 kardel * general update * * Revision 3.18 1994/10/03 21:59:22 kardel * 3.4e cleanup/integration * * Revision 3.17 1994/10/03 10:04:07 kardel * 3.4e reconcilation * * Revision 3.16 1994/05/31 20:02:40 kardel * sync on ONE transition * * Revision 3.15 1994/05/30 10:20:01 kardel * LONG cleanup * * Revision 3.14 1994/05/12 12:49:09 kardel * printf fmt/arg cleanup * * Revision 3.13 1994/03/10 19:00:43 kardel * clear utctime field to avoid confusion on synthesize time stamps * * Revision 3.12 1994/02/20 13:04:39 kardel * parse add/delete second support * * Revision 3.11 1994/02/02 17:45:23 kardel * rcs ids fixed * * Revision 3.9 1994/01/25 19:05:12 kardel * 94/01/23 reconcilation * * Revision 3.8 1994/01/22 11:24:11 kardel * fixed PPS handling * * Revision 3.7 1993/10/30 09:44:41 kardel * conditional compilation flag cleanup * * Revision 3.6 1993/10/03 19:10:45 kardel * restructured I/O handling * * Revision 3.5 1993/09/27 21:08:07 kardel * utcoffset now in seconds * * Revision 3.4 1993/09/26 23:40:25 kardel * new parse driver logic * * Revision 3.3 1993/09/01 21:44:54 kardel * conditional cleanup * * Revision 3.2 1993/07/09 11:37:18 kardel * Initial restructured version + GPS support * * Revision 3.1 1993/07/06 10:00:19 kardel * DCF77 driver goes generic... * */