/*
 * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A
 *
 * clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A
 *
 * Raw DCF77 pulse clock support
 *
 * Copyright (c) 1995-2006 by Frank Kardel <kardel <AT> ntp.org>
 * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */

#ifdef HAVE_CONFIG_H
# include <config.h>
#endif

/* define this for extensive printing / logging: */
/* #define FULL_DEBUG */

#if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_RAWDCF)

#include "ntp_fp.h"
#include "ntp_unixtime.h"
#include "ntp_calendar.h"

#include "parse.h"
#ifdef PARSESTREAM
# include <sys/parsestreams.h>
#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 (after 2003: data by Meteo Time GmbH)
 * 15		R     - after 2003:call bit (transmitter maintainance crew)
 * 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
 *
 * Note that there is one (maybe not so serious) deficiency in this
 * format: Leap seconds might only be inserted, not removed! First,
 * there is only an indicator for a leap second insertion; second, the
 * last bit in a regular datagram is a parity bit and might therefore
 * NOT be omitted! Should a leap second removal occur we have to drop
 * a complete datagram, making the clock disturbed for one minute.
 *
 * Since most simple receivers for DCF77 are rather susceptible for
 * radio noise the decoding does a lot of sanity and correlation checks
 * to make sure the datagram is valid.
 */

#define HISTO_BITS	9	/* size of histogram array (0..8 bits/byte)  */
#define PWLEN		8	/* PWM decoder histogram slots	             */

/* PCLEN must be a power of two, and PCMSK must be PCLEN - 1 */
#define PCLEN	       32	/* max number of pulse correlation values    */
#define PCMSK          31	/* mask for pulse correlation table          */
#define LOWAT          18	/* low watermark (go bad if below)           */
#define HIWAT          24       /* high watermark (go good if above)         */

/* TCLEN must be a power of two, and TCMSK must be TCLEN - 1 */
#define TCLEN	       16	/* max number of correlation values          */
#define TCMSK	       15	/* index mask for time correlation table     */

#define TIMEFUZZ       80	/* time compare fuzzyness in msec            */

static u_long pps_rawdcf(parse_t *, int, timestamp_t *);
static u_long cvt_rawdcf(u_char *, int, struct format *, clocktime_t *, void *);
static u_long inp_rawdcf(parse_t *, u_int, timestamp_t *);

/* --------------------------------------------------------------------
 * states of the internal frame scanner engine
 */
typedef enum {
	/* Initial state: no time stamp, no correlation data: */
	PS_INIT = 0,	/* Initial, no state available   */
	PS_IDLE,	/* wait for pulse correlator     */
	PS_WAIT,	/* wait for first sync gap       */
	PS_SYNC		/* machinery in sync with signal */
} pstate_t;

/* --------------------------------------------------------------------
 * Parse clock persistent state structure
 */

typedef struct {
	/* pulse correlator stuff. The tables are used as circular
	 * buffers to avoid moving memory; the price is an add and a
	 * mask operation for every index access.
	 */
	u_int32   pc_time[PCLEN]; /* pulse time table		*/
	u_char    pc_dist[PCLEN]; /* predecessor distance table */
	u_char    pc_head;	  /* head index of buffer       */
	u_char    pc_good;	  /* correlator OK / failed?    */

	/* timestamp correlation filter stuff. circular buffer. */
	int32     tc_time[TCLEN]; /* time code (minutes) table  */
	u_char    tc_dist[TCLEN]; /* predecessor distance table */
	u_char    tc_head;	  /* head index of buffer       */

	/* frame/sync decoder state machinery */
	pstate_t  ps_state;	/* What are we doing? */
	short     ps_leaps;	/* expect leap second (add or del) */
	u_int32   ps_start;	/* start time of frame             */
	u_long    ps_error;	/* eeror to feed to upper layer */

	/* PWM demodulator stuff */
	u_char pw_idx;		   	 /* current histogram slot */
	u_char pw_his[HISTO_BITS][PWLEN];/* histogram data         */

	/* pulse synthesizer gate */
	u_char st_gate;		/* enable synthetic stamps       */
	u_long st_flags;	/* clock time flags for sythesis */
} rawdcf_state_t;

/* --------------------------------------------------------------------
 * Parse clock data structure
 */
clockformat_t clock_rawdcf =
{
	inp_rawdcf,		/* DCF77 input handling			*/
	cvt_rawdcf,		/* raw dcf input conversion		*/
	pps_rawdcf,		/* examining PPS information		*/
	0,			/* no private configuration data	*/
	"RAW DCF77 Timecode",	/* direct decoding / time synthesis	*/
	62,			/* bit buffer				*/
	sizeof(rawdcf_state_t)
};

/* --------------------------------------------------------------------
 * For every bit in the DCF77 datagram we assign a printable
 * representation of the ONE value, the ZERO value, and a binary weight
 * of the bit. Unused and parity bits have bitweight of zero.
 */
typedef struct
{
	u_char repr1;		/* ASCII representation of ONE bit	*/
	u_char repr0;		/* ASCII representation of ZERO bit	*/
	u_char weight;		/* binary bit weight			*/
} bitinfo_t;

/* --------------------------------------------------------------------
 * Using the bit information and two vectors of bit group indices, we
 * get a complete description of a DCF77 datagram. We also use symbolic
 * indices for the groups.
 */

typedef struct
{
	bitinfo_t bitinfo[62];	/* vector for decoder info   		*/
	u_char    codetab[16];	/* start bit of info element 		*/
	u_char    partab [ 4];	/* start bit of parity group 		*/
} dcfparam_t;

static const dcfparam_t dcfparameter = {
    {
	/*  0 */{'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0},
	/*  4 */{'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0},
	/*  8 */{'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0},
	/* 12 */{'#', '-',  0}, {'#', '-',  0}, {'#', '-',  0}, {'R', '-',  1},
	/* 16 */{'A', '-',  1}, {'D', '-',  1}, {'M', '-',  2}, {'L', '-',  1},
	/* 20 */{'S', 's',  1}, {'1', '-',  1}, {'2', '-',  2}, {'4', '-',  4},
	/* 24 */{'8', '-',  8}, {'1', '-', 10}, {'2', '-', 20}, {'4', '-', 40},
	/* 28 */{'P', 'p',  0}, {'1', '-',  1}, {'2', '-',  2}, {'4', '-',  4},
	/* 32 */{'8', '-',  8}, {'1', '-', 10}, {'2', '-', 20}, {'P', 'p',  0},
	/* 36 */{'1', '-',  1}, {'2', '-',  2}, {'4', '-',  4}, {'8', '-',  8},
	/* 40 */{'1', '-', 10}, {'2', '-', 20}, {'1', '-',  1}, {'2', '-',  2},
	/* 44 */{'4', '-',  4}, {'1', '-',  1}, {'2', '-',  2}, {'4', '-',  4},
	/* 48 */{'8', '-',  8}, {'1', '-', 10}, {'1', '-',  1}, {'2', '-',  2},
	/* 52 */{'4', '-',  4}, {'8', '-',  8}, {'1', '-', 10}, {'2', '-', 20},
	/* 56 */{'4', '-', 40}, {'8', '-', 80}, {'P', 'p',  0}, {'#', '_',  0},
	/* 60 */{'#', '_',  0}, { 0 ,  0 ,  0}
    },{
	0, 15, 16, 17, 19, 20, 21, 28, 29, 35, 36, 42, 45, 50, 58, 59
    },{
	21, 29, 36, 59
    }
};

enum rawdcf_items {
	DCF_RES,
	DCF_R,
	DCF_A1,
	DCF_Z,
	DCF_A2,
	DCF_S,
	DCF_M,
	DCF_P1,
	DCF_H,
	DCF_P2,
	DCF_D,
	DCF_DW,
	DCF_MO,
	DCF_Y,
	DCF_P3
};

enum rawdcf_pgroups {
	DCF_P_P1,
	DCF_P_P2,
	DCF_P_P3
};

#define DCF_Z_MET 0x2
#define DCF_Z_MED 0x1

/* ====================================================================
 * Logging & Tracing (aka debug output...)
 * ====================================================================
 */
#ifdef PARSEKERNEL
# define MSYSLOG(args)
# define FSYSLOG(args)
#else
# ifdef FULL_DEBUG
#  define FSYSLOG(args) do { NLOG(NLOG_CLOCKINFO) msyslog args; } while (0)
# else
#  define FSYSLOG(args)
# endif
# define MSYSLOG(args) do { NLOG(NLOG_CLOCKINFO) msyslog args; } while (0)
#endif

#ifdef DEBUG
static void
_lprintf(
	const char *fmt,
	...
	)
{
	va_list va;
	if (debug >= DD_RAWDCF) {
		va_start(va, fmt);
		vprintf(fmt, va);
		va_end(va);
	}
}
# define LPRINTF(args) _lprintf args
#else
# define LPRINTF(args)
#endif

#if defined(DEBUG) && defined(FULL_DEBUG)
# define FPRINTF(args) _lprintf args
#else
# define FPRINTF(args)
#endif


/* ====================================================================
 * Time calculation stuff
 * ====================================================================
 *
 * Internal timeout and phase calculations are done in Q32.10 unsigned
 * fixed-point values. These would be done best in a monotonic time
 * scale, but it would require a monotonic time source and that's not
 * possible in a portable way; we use timestamps derived from the
 * receive time stamp instead.
 *
 * Note that system clock steps will most likely disrupt the frame
 * decoding and gets the frame decoder into initial state, so you're
 * bound to see a dysfunctional clock for up to two minutes when a clock
 * step occurs.
 */

#define MSEC_TO_Q10(x)	(((u_int32)(x) * 1024 + 500) / 1000)
#define Q10_TO_MSEC(x)	(((u_int32)(x) * 1000 + 512) / 1024)

#define Q10_TO_SEC(x)	(((u_int32)(x) + 512) / 1024)
#define SEC_TO_Q10(x)	((u_int32)(x) * 1024)

/* --------------------------------------------------------------------
 * Convert a 'timestamp_t' to a u_int32 millisecond counter.
 *
 * This is different for NTP or kernel module mode, since the timestamp
 * in use is either a 'struct timeval' or a 'l_fp'...
 */
static u_int32
time_from_timestamp(
	const timestamp_t  * stamp
	)
{
	u_int32 rtv;

#     ifdef PARSEKERNEL

	rtv = (u_int32)stamp->tv.tv_usec * 2147;
	rtv = (rtv >> 21)
	    + ((rtv & (1 << 20)) != 0)
	    + ((u_int32)stamp->tv.tv_sec << 10);

#     else

	rtv = (stamp->fp.l_uf >> 22)
	    + ((stamp->fp.l_uf & (1 << 21)) != 0)
	    + (stamp->fp.l_ui << 10);

#     endif

	return rtv;
}


/* ====================================================================
 * pulse correlator
 *
 * The pulse correlation filter tries to copy with out-of-phase or
 * additional pulses caused by interference or other problems with the
 * radio signal.
 *
 * Only pulses that are in phase to the absolute majority of the sampled
 * pulses can pass this filter.
 * ====================================================================
 *
 * reset pulse correlation filter
 */
static void
pulsecorr_rst(
	rawdcf_state_t * state)
{
	if (state->pc_head < PCLEN) {
		memset(state->pc_dist, 0xFF, sizeof(state->pc_dist));
		state->pc_head = PCLEN;
	}
}

/* --------------------------------------------------------------------
 * get correlated time difference from current to last pulse
 *
 * returns
 *  -2  if the correlator is in BAD state (no absolute majority detected)
 *  -1  There is a majority, but this pulse does not match it
 *  >1  distance to last pulse of majority group
 */
static int
pulsecorr_add(
	rawdcf_state_t * state,
	u_int32          value
	)
{
	/* get phase limits as 10-bit binary scaled fraction */
	static const u_int32 phase_lo = MSEC_TO_Q10(    TIMEFUZZ   );
	static const u_int32 phase_hi = MSEC_TO_Q10(1000 - TIMEFUZZ);

	size_t  i, j, k;     /* loop and table indices, unsigned */
	u_char  cmax;        /* index of maximum correlation     */
	u_char  cval[PCLEN]; /* correlation values               */
	u_short phase;       /* current phase difference (10 bit)*/

	/* enter new value into table. */
	state->pc_head    = i = (state->pc_head - 1) & PCMSK;
	state->pc_time[i] = value;
	state->pc_dist[i] = 0;

	/* skip trailing old values */
	for (j = (i - 1) & PCMSK; j != i; j = (j - 1) & PCMSK) {
		if (state->pc_dist[j] < PCLEN) {
			value = state->pc_time[i] - state->pc_time[j];
			if (value < (PCLEN << 11))
				break;
		}
		state->pc_dist[j] = PCLEN;
	}

	/* Search current maximum in old data and shortest distance for
	 * new pulse.
	 */
	for (cval[cmax = j] = 0; j != i; j = (j - 1) & PCMSK) {
		/* select greatest weight (newest on equal weight) */
		k = state->pc_dist[j];
		if (k > 0 && k < ((i - j) & PCMSK))
		    cval[j] = cval[(j + k) & PCMSK] + 1;
		else
			cval[j] = 0;
		if (cval[j] >= cval[cmax])
			cmax = j;

		/* find minimum match distance for new value */
		phase = (state->pc_time[i] - state->pc_time[j]) & 0x3FF;
		if (phase < phase_lo || phase >= phase_hi) {
			state->pc_dist[i] = (j - i) & PCMSK;
			cval[i] = cval[j] + 1;
		}
	}
	if (cval[i] >= cval[cmax])
		cmax = i;

	/* now check against current correlation maximum */
	if (cval[cmax] >= HIWAT && !state->pc_good) {
		state->pc_good = 1;
		FSYSLOG((LOG_INFO, "parse[dcf77]: pulse correlation state: bad --> good!"));
	}
	if (cval[cmax] < LOWAT && state->pc_good) {
		state->pc_good = 0;
		FSYSLOG((LOG_INFO, "parse[dcf77]: pulse correlation state: good --> bad!"));
	}
	if (!state->pc_good)
		return -2; /* phase correlator out of order */

	if (cmax != i) {
		value = state->pc_time[i] - state->pc_time[cmax];
		FSYSLOG((LOG_INFO,
			 "parse[dcf77]: pulse error: diff=%u (%u.%03us)",
			 value, Q10_TO_SEC(value), Q10_TO_MSEC(value & 0x3FF) ));
		return -1; /* this pulse is not good */
	}

	/* we have pulse in phase to a pulse train, get rounded seconds */
	k = (i + state->pc_dist[i]) & PCMSK;
	value = state->pc_time[i] - state->pc_time[k];
	return Q10_TO_SEC(value);
}


/* ====================================================================
 * time value correlator
 *
 * This code is used to guard against multi-bit errors in the decoded
 * DCF77 stream. Sometimes a datagram has so many bit errors that the
 * hamming distance of the parity check is not sufficient and the BCD
 * validity check does not catch the errors, either. This has been
 * observed to happen temporarily during thunderstorms and other
 * environmental events that degraded signal quality.  Since the decoded
 * time stamp is patently wrong in this case, additional constraints are
 * needed to validate the time code.
 *
 * This attempt uses a variation of a correlation filter: It takes up to
 * TCLEN samples and tries to identify the time code wich has the
 * maximum number of matching predecessors. If the correlation is high
 * or recently new then either the value itself is reproduced or a
 * synthetic value is created from the correlation maximum. Otherwise ~0
 * is returned to indicate an invalid result.
 * ====================================================================
 *
 * reset time correlator data
 */
static void
timecorr_rst(
	rawdcf_state_t * state
	)
{
	if (state->tc_head < TCLEN) {
		memset(state->tc_dist, 0xFF, sizeof(state->tc_dist));
		state->tc_head = TCLEN;
	}
}

/* --------------------------------------------------------------------
 * get correlated time stamp from input value
 *
 * This algoritm is O(TCLEN); it does so by some bookkeping so previous
 * results can be reused.
 *
 * returns ~(time_t)0 on error, or a time stamp that is an exact
 * on-minute value.
 */
static time_t
timecorr_add(
	rawdcf_state_t * state,
	time_t           value
	)
{
	size_t i, j, k; /* loop and table indices, unsigned	*/
	u_char cmax;	/* index of maximum correlation */
	u_char cval[TCLEN];

	/* enter new value into table */
	state->tc_head    = i = (state->tc_head - 1) & TCMSK;
	state->tc_time[i] = (~value) ? (int32)(value / 60) : ~(int32)0;
	state->tc_dist[i] = 0;

	/* skip trailing invalid (NaN) entries */
	for (j = (i - 1) & TCMSK; j != i; j = (j - 1) & TCMSK) {
		if (state->tc_dist[j] < TCLEN && ~state->tc_time[j])
			break;
		state->tc_dist[j] = ~(u_char)0;
	}

	/* search current maximum in old data and get shortest distance
	 * for new entry.
	 */
	for (cval[cmax = j] = 0;  j != i;  j = (j - 1) & TCMSK) {
		/* select greatest weight (newest on equal weight) */
		k = state->tc_dist[j];
		if (k > 0 && k < ((i - j) & TCMSK))
			cval[j] = cval[(j + k) & TCMSK] + 1;
		else
			cval[j] = 0;
		if (cval[j] >= cval[cmax])
			cmax = j;

		/* find shortes distance path for new entry */
		k = (j - i) & TCMSK;
		if (state->tc_time[i] - state->tc_time[j] == k) {
			state->tc_dist[i] = (u_char)k;
			cval[i] = cval[j] + 1;
		}
	}
	if (cval[i] >= cval[cmax])
		cmax = i;
	k = (cmax - i) & TCMSK; /* distance head --> max */

	/* get output value */
	if (k == 0 && ~state->tc_time[i])
		/* best case: maximum at top of pile. use it. */
		value = (time_t)state->tc_time[i] * 60;
	else if (cval[cmax] > k)
		/* some inconsitency, but we can live with it. */
		value = (time_t)(state->tc_time[cmax] + k) * 60;
	else
		/* too much garbage, take a break. */
		value = ~(time_t)0;

	return value;
}


/* ====================================================================
 * PWM decoder related functions
 * ====================================================================
 *
 * get number of bits in an unsigned integral value. There are many ways
 * to do this...
 */

#if defined(__GNUC__) && __GNUC__ >= 4
# define popcount(x) __builtin_popcount((x))
#else

static size_t
popcount(
	u_long x
	)
{
	/* Use table for lookup: Every byte contains the PW value
	 * for the corresponding nibble.
	 */
	static const u_char table [16] = {
		0, 1, 1, 2, 1, 2, 2, 3,
		1, 2, 2, 3, 2, 3, 3, 4 };

	size_t n;
	for (n = 0; x != 0; x >>= 4)
		n += table[x & 0x0F];
	return n;
}

#endif

/* --------------------------------------------------------------------
 * select new PWM histogram slot in the collection table
 */
static void
pwm_next_slot(
	rawdcf_state_t * state
	)
{
	size_t bits;

	/* select next histogram slot, with proper wrap-around */
	if (++state->pw_idx >= PWLEN)
		state->pw_idx = 0;

	/* reset the slot values */
	for (bits = 0; bits < HISTO_BITS; bits++)
		state->pw_his[bits][state->pw_idx] = 0;
}

/* --------------------------------------------------------------------
 * update PWM histogram data
 */
static void
pwm_add_sample(
	rawdcf_state_t * state,
	u_char           value
	)
{
	/* Add a new sample to the current histogram slot. If the slot
	 * becomes full, select the next slot for future use.
	 */
	if (value < HISTO_BITS)
		if (++state->pw_his[value][state->pw_idx] > 200)
			pwm_next_slot(state); /* rotate tables... */
}

/* --------------------------------------------------------------------
 * evaluate PWM data to get cutoff value
 */
static size_t
pwm_get_cutoff(
	const rawdcf_state_t * state
	)
{
	size_t cutoff, samples, bitsum, avglo, avghi, minlo, minhi;
	size_t histo[HISTO_BITS];
	size_t mhold, i, j;

	/* get total sums from histogram slices */
	for (i = 0; i < HISTO_BITS; i++)
		for (histo[i] = j = 0; j < PWLEN; j++)
			histo[i] += state->pw_his[i][j];

	/* Get the initial cutoff value as the mean bitcount of the
	 * whole histogram. Make sure we have enough samples while we
	 * go, and calculate the mean with proper rounding as we do not
	 * have that many histogram bins.
	 */
	bitsum = samples = 0;
	for (i = 0; i < HISTO_BITS; i++) {
		samples += histo[i];
		bitsum  += histo[i] * i;
	}
	if (samples < 60)
		return 0;
	avglo = avghi = cutoff = (bitsum + samples / 2) / samples;

	/* Get the ceiling of the mean bitcount in the lower part of
	 * the histogram. This is the mean bit count for short
	 * pulses.
	 */
	bitsum = samples = 0;
	for (i = 0; i < cutoff; i++) {
		samples += histo[i];
		bitsum  += histo[i] * i;
	}
	if (bitsum)
		avglo = (bitsum + samples - 1) / samples;

	/* Get the floor of mean bitcount in upper part of the
	 * histogram. This is the mean bitcount for long pulses.
	 */
	bitsum = samples = 0;
	for (i = cutoff; i < HISTO_BITS; i++) {
		samples += histo[i];
		bitsum  += histo[i] * i;
	}
	if (bitsum)
		avghi = bitsum / samples;

#     ifdef DEBUG
	LPRINTF(("parse[dcf77]: pwm_get_cutoff: histogram:"));
	for (i = 0; i < HISTO_BITS; i++)
		LPRINTF((" %lu", (u_long)histo[i]));
	LPRINTF(("\nparse[dcf77]: pwm_get_cutoff: mean=%lu, mean(lo)=%lu, mean(hi)=%lu\n",
		 (u_long)cutoff, (u_long)avglo, (u_long)avghi));
#     endif

	/* Search the histogram minimum between low and high mean
	 * values. This can actually be a plateau, so make sure we have
	 * the edges where the values start to increase again. Then
	 * calculate the actual cutoff as the center of the plateau.
	 */
	minlo = minhi = avglo;
	mhold = histo[minlo];
	for (i = avglo + 1; i <= avghi; i++) {
		if (histo[i] < mhold) {
			mhold = histo[i];
			minlo = minhi = i;
		} else if (histo[i] == mhold) {
			minhi = i;
		}
	}
	cutoff = (minhi + minlo + 1) / 2;
	LPRINTF(("parse[dcf77]: pwm_get_cutoff: minlo=%lu minhi=%lu cutoff=%lu\n",
		 (u_long)minlo, (u_long)minhi, (u_long)cutoff));

	return cutoff;
}


/* ====================================================================
 * Datagram decoding stuff
 * ====================================================================
 *
 * Extract a bitfield
 *
 * Given a bit-decoded buffer and a group index, scan the bits from the
 * group until (but excluding) the first bit of the following group. The
 * bit weights are taken from the bitweight vector, and bits that match
 * the '1' representation are added according to their weight.
 */
static u_long
ext_bf(
	u_char	         * buf,
	int		   idx,
	const dcfparam_t * dcfp
)
{
	u_long	         sum;
	int		 i;
	const bitinfo_t *v;

	i    = dcfparameter.codetab[idx];
	v    = dcfp->bitinfo + i;
	buf += i;
	i    = dcfparameter.codetab[idx+1] - i;

	for (sum=0; i; --i,++buf,++v)
		if (*buf == v->repr1)
			sum += v->weight;

	return sum;
}

/* --------------------------------------------------------------------
 * Check a group parity
 *
 * Given a bit-decoded buffer and a group index, scan the bits from the
 * group until (but excluding) the first bit of the following
 * group. Everything that is not a ZERO character is considered a '1'
 * bit, and the function returns TRUE if the number of '1' bits
 * (including the parity bit) is even in a group.
 */
static u_int
pcheck(
	u_char	         * buf,
	int		   idx,
	const dcfparam_t * dcfp
)
{
	u_int	         psum;
	int		 i;
	const bitinfo_t *v;

	i    = dcfparameter.partab[idx];
	v    = dcfp->bitinfo + i;
	buf += i;
	i    = dcfparameter.partab[idx+1] - i;

	for (psum=1; i; --i,++buf,++v)
		psum ^= (*buf == v->repr1);

	return psum;
}

/* --------------------------------------------------------------------
 * Check a group for a valid BCD encoding
 */
static u_int
bcdcheck(
	u_char	         * buf,
	int		   idx,
	const dcfparam_t * dcfp
)
{
	u_char	         bcd;
	int		 i, n;
	const bitinfo_t *v;

	n    = dcfparameter.codetab[idx];
	v    = dcfp->bitinfo + n;
	buf += n;
	n    = dcfparameter.codetab[idx+1] - n;
	bcd  = 0;

	/* Shift in bits, LSB first. For every nibble check if the
	 * value is < 10.
	 */
	for (i = 0; i < n && bcd < 10; i++,buf++,v++)
		if (0 == (i & 3))
			bcd  = (*buf == v->repr1);
		else
			bcd |= (*buf == v->repr1) << (i & 3);

	return bcd < 10;
}

/* --------------------------------------------------------------------
 * Pulse width decoder
 *
 * This function does the PWM decoding of the 59 regular bits of a raw
 * DCF77 datagram.
 */
static u_long
decode_rawdcf(
	const rawdcf_state_t * state,
	const bitinfo_t      * bits,
	u_char	             * buffer,
	int	               size
)
{
	u_char cutoff;
	u_long rtc;

	cutoff = pwm_get_cutoff(state);
	if (0 == cutoff) {
		/* We cannot decode, but can make the buffer printable. */
		rtc = CVT_NONE;
		for (/*NOP*/; size > 0 && bits->repr1; buffer++, bits++, size--) {
			if (*buffer >= HISTO_BITS)
				*buffer = '?';
			else
				*buffer += '0';
		}
	} else {
		/* Do PWM decoding here. Sanity checks are done later! */
		rtc = CVT_OK;
		for (/*NOP*/; size > 0 && bits->repr1; buffer++, bits++, size--) {
			if (*buffer >= HISTO_BITS) {
				rtc = CVT_FAIL|CVT_BADFMT;
				*buffer = '?';
			} else if (*buffer >= cutoff) {
				*buffer = bits->repr1;
			} else {
				*buffer = bits->repr0;
			}
		}
	}
	*buffer = '\0'; /* make sure it's terminated... */

	return rtc;
}

/* --------------------------------------------------------------------
 * Decode the time stamp of a DCF77 datagram
 */
static u_long
convert_rawdcf(
	u_char	         * buffer,
	int		   size,
	const dcfparam_t * dcfprm,
	clocktime_t	 * clock_time
)
{
	int tzval;

	LPRINTF(("parse[dcf77]: convert_rawdcf: \"%s\"\n", buffer));

	/* Check Start and Parity bits */
	if ( ! (ext_bf(buffer, DCF_S   , dcfprm) &&
		pcheck(buffer, DCF_P_P1, dcfprm) &&
		pcheck(buffer, DCF_P_P2, dcfprm) &&
		pcheck(buffer, DCF_P_P3, dcfprm)  ))
	{
		/* bad format - not for us */
		MSYSLOG((LOG_ERR,
			 "parse[dcf77]: convert_rawdcf: frame check FAILED for \"%s\"",
			 buffer));
		return CVT_FAIL|CVT_BADFMT;
	}

	/* Check BCD encoding for date and time fields
	 *
	 * This seems to fail very seldom, which leads to the conclusion
	 * that most interference shortens pulses instead of prolonging
	 * them, flipping bits from '1' to '0'. (Which makes sense, as a
	 * pulse is created by amplitude reduction, and additional
	 * energy outbursts in the signal spectrum increase the
	 * amplitude again... Still, it doesn't hurt to have this
	 * safeguard.)
	 */
	if ( ! (bcdcheck(buffer, DCF_M,  dcfprm) &&
		bcdcheck(buffer, DCF_H,  dcfprm) &&
		bcdcheck(buffer, DCF_D,  dcfprm) &&
		bcdcheck(buffer, DCF_MO, dcfprm) &&
		bcdcheck(buffer, DCF_Y,  dcfprm)  ))
	{
		MSYSLOG((LOG_ERR,
			 "parse[dcf77]: convert_rawdcf: BCD value check FAILED for \"%s\"",
			 buffer));
		return CVT_FAIL|CVT_BADDATE;
	}

	/* buffer seems OK, some final checks follow later */
	LPRINTF(("parse[dcf77]: convert_rawdcf: parity & BCD check passed\n"));

	clock_time->flags  = PARSEB_S_LEAP;
	clock_time->utctime= 0;
	clock_time->usecond= 0;
	clock_time->second = 0;
	clock_time->minute = ext_bf(buffer, DCF_M,  dcfprm);
	clock_time->hour   = ext_bf(buffer, DCF_H,  dcfprm);
	clock_time->day    = ext_bf(buffer, DCF_D,  dcfprm);
	clock_time->month  = ext_bf(buffer, DCF_MO, dcfprm);
	clock_time->year   = ext_bf(buffer, DCF_Y,  dcfprm);
	tzval              = ext_bf(buffer, DCF_Z,  dcfprm);

	/* value consistency for date/time is checked by framework;
	 * don't care about it here!
	 */

	/* check time zone info */
	switch (tzval)
	{
	case DCF_Z_MET:
		clock_time->utcoffset = -1*60*60;
		break;

	case DCF_Z_MED:
		clock_time->flags     |= PARSEB_DST;
		clock_time->utcoffset  = -2*60*60;
		break;

	default:
		MSYSLOG((LOG_ERR,
			 "parse[dcf77]: convert_rawdcf: BAD TIME ZONE TAG: %d", tzval));
		return CVT_FAIL|CVT_BADFMT;
	}

	/* fetch remaining flag bits */
	if (ext_bf(buffer, DCF_A1, dcfprm))
		clock_time->flags |= PARSEB_ANNOUNCE;
	if (ext_bf(buffer, DCF_A2, dcfprm))
		clock_time->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
	if (ext_bf(buffer, DCF_R, dcfprm))
		clock_time->flags |= PARSEB_MESSAGE; /* operator call bit */

	LPRINTF(("parse[dcf77]: convert_rawdcf: TIME CODE OK:"
		 " %02ld:%02ld, %02ld.%02ld.%02ld, flags 0x%lx\n",
		 clock_time->hour, clock_time->minute,
		 clock_time->day, clock_time->month, clock_time->year,
		 clock_time->flags));

	return CVT_OK;
}


/* ====================================================================
 * Parser framework-related functions
 * ====================================================================
 *
 * cvt_rawdcf -- convert input data
 *
 * raw dcf input conversion routine - needs to fix up the pulse width
 * data for 1/0 decision
 */
static u_long
cvt_rawdcf(
	u_char	      * buffer,
	int	        size,
	struct format * param,
	clocktime_t   * clock_time,
	void	      * local
)
{
	rawdcf_state_t * state   = (rawdcf_state_t *)local;
	u_long	         rtc     = CVT_NONE;
	time_t	         newtime = ~(time_t)0;
	time_t	         dcftime = ~(time_t)0;
	int              minute;

	/*
	* Make sure we have a readable representation of what we've got until
	* now; sanity checks will follow on.
	*/
	rtc = decode_rawdcf(state, dcfparameter.bitinfo, buffer, size);
	if (state->ps_error) {
		rtc = state->ps_error;
		state->ps_error = 0;
	} else if (rtc & CVT_FAIL) {
		/* Error in PWM decoding --- do not process the data buffer. */
		MSYSLOG((LOG_ERR,
			 "parse[dcf77]: cvt_rawdcf: BAD DATA - no conversion for \"%s\"",
			 buffer));
		//rtc = CVT_NONE;
	} else if (size < 59) {
		/* The timecode is incomplete --- do not process the buffer. */
		MSYSLOG((LOG_ERR,
			 "parse[dcf77]: cvt_rawdcf: INCOMPLETE DATA - time code only has %d bits",
			 size));
		rtc = CVT_FAIL | CVT_BADFMT;
	} else {
		/* Looks good, decode bit stream and get time stamp */
		FSYSLOG((LOG_INFO,
			 "parse[dcf77]: cvt_rawdcf: good timecode \"%s\"",
			 buffer));
		rtc = convert_rawdcf(buffer, size, &dcfparameter, clock_time);
		if (rtc == CVT_OK) {
			dcftime = parse_to_unixtime(clock_time, &rtc);
			pwm_next_slot(state);
		}
	}

	/* There is some hope to get things right, either because we
	 * have a good conversion result or we might rely on some
	 * correlation data.
	 *
	 * So, first we push the new time stamp (which might be bad)
	 * through the time correlation filter. This can give us back a
	 * valid time stamp which we will use, or an invalid time stamp,
	 * in which case we are lost...
	 */
	newtime = timecorr_add(state, dcftime);

	if (0 == ~newtime) {
		/* Failed to get a new time stamp... */
		if (0 != ~dcftime) {
			if (rtc == CVT_OK)
				rtc = CVT_FAIL|CVT_BADTIME;
			MSYSLOG((LOG_ERR,
				 "parse[dcf77]: cvt_rawdcf: UNSTABLE TIME CORRELATION"));
		}
		return rtc;
	}

	/* We have a new time stamp. If it's synthetic (from the time
	 * correlation, not from the datagram) we have to keep track of
	 * the status bits ourself!
	 */
	minute = (newtime % 3600) / 60; /* get minute of hour */
	if (newtime == dcftime) {
		/* updae remembered state from time stamp */
		state->st_flags = clock_time->flags;
	} else {
		MSYSLOG((LOG_INFO,
			 "parse[dcf77]: cvt_rawdcf: using synthetic time"));

		/* At the beginning of a new hour, reset the ANNOUNCE and
		 * LEAPS bits.  Eventually toggle the DST bit, if
		 * ANNOUNCE was set. This will probably cause less
		 * trouble than keeping the old values!
		 */
		if (0 == minute) {
			if (state->st_flags & PARSEB_ANNOUNCE)
				state->st_flags ^= PARSEB_DST;
			state->st_flags &= ~(PARSEB_ANNOUNCE|PARSEB_LEAPS);
		}
		clock_time->flags = state->st_flags;
	}
	/* If we have a pending leap insert warning and this is the last
	 * minute of an hour, make sure the frame decoder takes one bit
	 * more (or one less) or frame sync errors will occur!
	 */
	if ((clock_time->flags & PARSEB_LEAPS) && 59 == minute)
		state->ps_leaps = 1; /* expect leap second add or del */

	state->st_gate = 1; /* synthetic second stamps ON */
	clock_time->utctime = newtime;
	rtc = CVT_OK;
	LPRINTF(("parse[dcf77]: decode_rawdcf: good time\n"));

	return 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(
	parse_t	    *parseio,
	int	     status,
	timestamp_t *ptime
)
{
	if (!status) {		/* negative edge for simpler wiring (Rx->DCD) */
		parseio->parse_dtime.parse_ptime  = *ptime;
		parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
	}

	return CVT_NONE;
}

/*---------------------------------------------------------------------
 * snt_rawdcf -- create interpolated time stamps
 *
 * synthesize time stamps if the clock is up and running, so we get more
 * than one timestamp per minute.
 */
static u_long
snt_rawdcf(
	parse_t * parseio,
	int       delta
	)
{
	u_long           rtc   = PARSE_INP_SKIP;
	rawdcf_state_t * state = (rawdcf_state_t*)parseio->parse_pdata;

	if (state->st_gate) {
#             ifdef PARSEKERNEL
		parseio->parse_dtime.parse_time.tv.tv_sec += delta;
#             else
		parseio->parse_dtime.parse_time.fp.l_ui += delta;
#             endif

		FPRINTF(("parse[dcf77]: snt_rawdcf: time stamp synthesized (at %ds)\n",
			 parseio->parse_index  - 1));
		/*
		 * Make sure we have the same time status as we had on
		 * the last pulse or the full-minute telegram. The
		 * 'PARSEB_TIMECODE' bit will be set anyway.
		 */
		updatetimeinfo(parseio, state->st_flags);
		rtc = PARSE_INP_SYNTH;
	}
	return rtc;
}

/*---------------------------------------------------------------------
 * inp_rawdcf -- process raw data input
 *
 * Grab DCF77 data from input stream. This uses a state machine and
 * pulse phase checking to avoid trouble caused by stray pulses and/or
 * missing pulses. The PWM decoding using a UART @ 50bd is cheap; we
 * cannot expect wonders here. We can only try to avoid the worst
 * problems, and it's probably better to set the clock into a FAIL state
 * instead of happily producing wrong time stamps that will upset the
 * system.
 *
 * Also note that we have two levels of checks here: First we deal with
 * the formal correctness of the pulse timing, because if we are
 * out-of-sync in relation to the sync marks we should not try to feed
 * synthetic time stamps or evaluate the datagrams, as they are assumed
 * to be on the change of minutes.
 *
 * Once we have a consecutive synchronisation, we might gloss over
 * decoding errors for a few datagrams: We still are in sync with the
 * per-minute signal and can therefore interpolate to a small degree over
 * datagrams that cannot be decoded or, even worse, pass all formal
 * checks but have still a damaged time stamp.
 */
static u_long
inp_rawdcf(
	parse_t     *parseio,
	u_int	     ch,
	timestamp_t *tstamp
)
{
	u_long	         rtc   = PARSE_INP_SKIP;
	rawdcf_state_t * state = (rawdcf_state_t*)parseio->parse_pdata;
	u_int32          tnow;
	int              sdiff, tdiff;

	/* Do some accountant's chores about time stamps; this includes
	 * exercising the pulse correlation filter.
	 */
	parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
	tnow = time_from_timestamp(tstamp);	    /* get pulse time */

	/* Transform input char into bitcount or error tag and feed it
	 * the PWM pulse width statistics.
	 */
	ch ^= 0x00FFU;	/* invert bit pattern -- LOW BITS ONLY! */
	if ( ! (ch & (ch + 1))) {	/* check for bit train */
		ch = popcount(ch);	/* yes -> get train length */
		pwm_add_sample(state, ch);
	} else {
		ch = ~(u_char)0;	/* no -> invalid bit count */
	}

	/* Do one step in the state machine. */
	switch (state->ps_state)
	{
	default:
	case PS_INIT:
		/* Initial state: Just clear all internal state and
		 * remember this timestamp. Force Input conversion on
		 * the invalid data to get the clock state right.
		 */
		timecorr_rst(state);
		pulsecorr_rst(state);
		pulsecorr_add(state, tnow);
		state->ps_state = PS_IDLE;	/* wait for pulses from correlator */
		FSYSLOG((LOG_INFO,
			 "parse[dcf77]: inp_rawdcf: PS_INIT --> PS_IDLE"));
		break;

	case PS_IDLE:
		sdiff = pulsecorr_add(state, tnow);	    /* correlated difference */
		if (sdiff > 0) {
			state->ps_error = CVT_SKIP;
			state->ps_state = PS_WAIT;	/* initial sync hunt state */
			parse_end(parseio);		/* flush input buffer ... */
			parse_end(parseio);
			rtc = PARSE_INP_TIME;		/* ... and process it. */
			FSYSLOG((LOG_INFO,
				 "parse[dcf77]: inp_rawdcf: PS_INIT --> PS_WAIT"));
		}
		break;

	case PS_WAIT:
		/* Wait for first sync gap: If we are on time two
		 * seconds after the last pulse, we have a sync mark and
		 * start sampling immediately. Otherwise just keep in
		 * this state.
		 */
		sdiff = pulsecorr_add(state, tnow);	    /* correlated difference */
		if (sdiff == 2) {
			/* hit sync pulse, start */
			parse_addchar(parseio, ch);
			state->ps_leaps = 0;		/* no leap second pending */
			state->ps_start = tnow;
			state->ps_state = PS_SYNC;
			state->st_gate  = 0;		/* no synthetic time stamps, either */
			FSYSLOG((LOG_INFO,
				 "parse[dcf77]: inp_rawdcf: PS_WAIT --> PS_SYNC"));
		}
		break;

	case PS_SYNC:
		/* Collect data and check frame consistency on the fly. */
		sdiff = pulsecorr_add(state, tnow);	    /* correlated difference */
		tdiff = Q10_TO_SEC(tnow - state->ps_start);
		if (tdiff > 60 + state->ps_leaps) {
			/* overrun detected, go back hunting... */
			FSYSLOG((LOG_ERR,
				 "parse[dcf77]: inp_rawdcf: frame overrun"));
			goto restart;
		}

		switch (sdiff)
		{
		case 3:	/* emergency only -- we might have lost the last pulse! */
		case 2:	/* possible sync or missing signal. */
			if (tdiff == 60 - state->ps_leaps ||
			    tdiff == 60 + state->ps_leaps ) {
				/* definitely start of new minute. */
				FSYSLOG((LOG_INFO,
					 "parse[dcf77]: inp_rawdcf: regular sync detected, gap = %ds\n",
						sdiff));
				rtc = parse_end(parseio);
				parse_addchar(parseio, ch);
				state->ps_start = tnow;
				state->ps_leaps = 0;
				state->st_gate  = 0;
				break;
			}
			/* FALLTHROUGH */
		case 1:
			/* We either have a regular pulse, or we might
			 * have encountered a drop that was not on time
			 * to be a sync pulse. Try to catch up with any
			 * missing bits caused by dropped pulses, then
			 * add the new sample and eventually create a
			 * synthetic time sample. But do not allow time
			 * to warp backwards!
			 */
			if (parseio->parse_index > tdiff) {
				MSYSLOG((LOG_ERR,
					 "parse[dcf77]: inp_rawdcf: time warp into past?"));
				goto restart;
			}
			rtc = snt_rawdcf(parseio, tdiff - parseio->parse_index + 1);
			while (parseio->parse_index < tdiff) {
				FSYSLOG((LOG_INFO,
					 "parse[dcf77]: inp_rawdcf: add missing pulse #%d",
					 parseio->parse_index));
				parse_addchar(parseio, ~(u_char)0);
			}
			parse_addchar(parseio, ch);
			break;

		case 0:
			FSYSLOG((LOG_INFO,
				 "parse[dcf77]: inp_rawdcf: double-beat ignored"));
			break;

		case -1:
			FSYSLOG((LOG_INFO,
				 "parse[dcf77]: inp_rawdcf: out-of-phase pulse ignored"));
			break;

		case -2:
			MSYSLOG((LOG_ERR,
				 "parse[dcf77]: inp_rawdcf: pulse correlation failed"));
			state->ps_error = CVT_FAIL|CVT_BADFMT;
			state->ps_state = PS_IDLE;	/* initial wait for cerrelator */
			timecorr_rst(state);		/* minute sync lost, data invalid */
			rtc = parse_end(parseio);
			break;

		default:
			/* we are in trouble here... */
			MSYSLOG((LOG_ERR,
				 "parse[dcf77]: inp_rawdcf: missing %d pulses",
					sdiff - 1));
			goto restart;
		}
		break;

	restart:
		/* Emergency recovery -- reset all state but PWM
		 * histogram and pulse correlator. Force Input
		 * conversion on the invalid data to get the clock state
		 * right.
		 */
		FSYSLOG((LOG_INFO,
			 "parse[dcf77]: inp_rawdcf: * --> PS_WAIT (restart)"));
		state->ps_error = CVT_FAIL|CVT_BADFMT;
		state->ps_state = PS_WAIT;	/* initial sync hunt state */
		timecorr_rst(state);		/* minute sync lost, data invalid */
		rtc = parse_end(parseio);
		break;
	}

	return rtc;
}

#else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */
int clk_rawdcf_bs;
#endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */

/*
 * History:
 *
 * clk_rawdcf.c,v
 * Revision 4.18  2006/06/22 18:40:01  kardel
 * clean up signedness (gcc 4)
 *
 * Revision 4.17  2006/01/22 16:01:55  kardel
 * update version information
 *
 * Revision 4.16  2006/01/22 15:51:22  kardel
 * generate reasonable timecode output on invalid input
 *
 * Revision 4.15  2005/08/06 19:17:06  kardel
 * clean log output
 *
 * Revision 4.14  2005/08/06 17:39:40  kardel
 * cleanup size handling wrt/ to buffer boundaries
 *
 * Revision 4.13  2005/04/16 17:32:10  kardel
 * update copyright
 *
 * Revision 4.12  2004/11/14 15:29:41  kardel
 * support PPSAPI, upgrade Copyright to Berkeley style
 *
 * Revision 4.9  1999/12/06 13:42:23  kardel
 * transfer correctly converted time codes always into tcode
 *
 * Revision 4.8  1999/11/28 09:13:50  kardel
 * RECON_4_0_98F
 *
 * Revision 4.7  1999/04/01 20:07:20  kardel
 * added checking for minutie increment of timestamps in clk_rawdcf.c
 *
 * Revision 4.6  1998/06/14 21:09:37  kardel
 * Sun acc cleanup
 *
 * Revision 4.5  1998/06/13 12:04:16  kardel
 * fix SYSV clock name clash
 *
 * Revision 4.4  1998/06/12 15:22:28  kardel
 * fix prototypes
 *
 * Revision 4.3  1998/06/06 18:33:36  kardel
 * simplified condidional compile expression
 *
 * Revision 4.2  1998/05/24 11:04:18  kardel
 * triggering PPS on negative edge for simpler wiring (Rx->DCD)
 *
 * Revision 4.1  1998/05/24 09:39:53  kardel
 * implementation of the new IO handling model
 *
 * Revision 4.0  1998/04/10 19:45:30  kardel
 * Start 4.0 release version numbering
 *
 * from V3 3.24 log info deleted 1998/04/11 kardel
 *
 */
