xref: /freebsd/contrib/ntp/libparse/clk_rawdcf.c (revision ceaec73d406831b1251babb61675df0a1aa54a31)
1 /*
2  * /src/NTP/ntp-4/libparse/clk_rawdcf.c,v 4.9 1999/12/06 13:42:23 kardel Exp
3  *
4  * clk_rawdcf.c,v 4.9 1999/12/06 13:42:23 kardel Exp
5  *
6  * Raw DCF77 pulse clock support
7  *
8  * Copyright (C) 1992-1998 by Frank Kardel
9  * Friedrich-Alexander Universit�t Erlangen-N�rnberg, Germany
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  *
15  */
16 
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 
21 #if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_RAWDCF)
22 
23 #include "ntp_fp.h"
24 #include "ntp_unixtime.h"
25 #include "ntp_calendar.h"
26 
27 #include "parse.h"
28 #ifdef PARSESTREAM
29 # include <sys/parsestreams.h>
30 #endif
31 
32 #ifndef PARSEKERNEL
33 # include "ntp_stdlib.h"
34 #endif
35 
36 /*
37  * DCF77 raw time code
38  *
39  * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
40  * und Berlin, Maerz 1989
41  *
42  * Timecode transmission:
43  * AM:
44  *	time marks are send every second except for the second before the
45  *	next minute mark
46  *	time marks consist of a reduction of transmitter power to 25%
47  *	of the nominal level
48  *	the falling edge is the time indication (on time)
49  *	time marks of a 100ms duration constitute a logical 0
50  *	time marks of a 200ms duration constitute a logical 1
51  * FM:
52  *	see the spec. (basically a (non-)inverted psuedo random phase shift)
53  *
54  * Encoding:
55  * Second	Contents
56  * 0  - 10	AM: free, FM: 0
57  * 11 - 14	free
58  * 15		R     - alternate antenna
59  * 16		A1    - expect zone change (1 hour before)
60  * 17 - 18	Z1,Z2 - time zone
61  *		 0  0 illegal
62  *		 0  1 MEZ  (MET)
63  *		 1  0 MESZ (MED, MET DST)
64  *		 1  1 illegal
65  * 19		A2    - expect leap insertion/deletion (1 hour before)
66  * 20		S     - start of time code (1)
67  * 21 - 24	M1    - BCD (lsb first) Minutes
68  * 25 - 27	M10   - BCD (lsb first) 10 Minutes
69  * 28		P1    - Minute Parity (even)
70  * 29 - 32	H1    - BCD (lsb first) Hours
71  * 33 - 34      H10   - BCD (lsb first) 10 Hours
72  * 35		P2    - Hour Parity (even)
73  * 36 - 39	D1    - BCD (lsb first) Days
74  * 40 - 41	D10   - BCD (lsb first) 10 Days
75  * 42 - 44	DW    - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
76  * 45 - 49	MO    - BCD (lsb first) Month
77  * 50           MO0   - 10 Months
78  * 51 - 53	Y1    - BCD (lsb first) Years
79  * 54 - 57	Y10   - BCD (lsb first) 10 Years
80  * 58 		P3    - Date Parity (even)
81  * 59		      - usually missing (minute indication), except for leap insertion
82  */
83 
84 static u_long pps_rawdcf P((parse_t *, int, timestamp_t *));
85 static u_long cvt_rawdcf P((unsigned char *, int, struct format *, clocktime_t *, void *));
86 static u_long inp_rawdcf P((parse_t *, unsigned int, timestamp_t  *));
87 
88 typedef struct last_tcode {
89 	time_t tcode;	/* last converted time code */
90 } last_tcode_t;
91 
92 clockformat_t clock_rawdcf =
93 {
94   inp_rawdcf,			/* DCF77 input handling */
95   cvt_rawdcf,			/* raw dcf input conversion */
96   pps_rawdcf,			/* examining PPS information */
97   0,				/* no private configuration data */
98   "RAW DCF77 Timecode",		/* direct decoding / time synthesis */
99 
100   61,				/* bit buffer */
101   sizeof(last_tcode_t)
102 };
103 
104 static struct dcfparam
105 {
106 	unsigned char onebits[60];
107 	unsigned char zerobits[60];
108 } dcfparameter =
109 {
110 	"###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */
111 	"--------------------s-------p------p----------------------p"  /* 'ZERO' representation */
112 };
113 
114 static struct rawdcfcode
115 {
116 	char offset;			/* start bit */
117 } rawdcfcode[] =
118 {
119 	{  0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
120 	{ 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
121 };
122 
123 #define DCF_M	0
124 #define DCF_R	1
125 #define DCF_A1	2
126 #define DCF_Z	3
127 #define DCF_A2	4
128 #define DCF_S	5
129 #define DCF_M1	6
130 #define DCF_M10	7
131 #define DCF_P1	8
132 #define DCF_H1	9
133 #define DCF_H10	10
134 #define DCF_P2	11
135 #define DCF_D1	12
136 #define DCF_D10	13
137 #define DCF_DW	14
138 #define DCF_MO	15
139 #define DCF_MO0	16
140 #define DCF_Y1	17
141 #define DCF_Y10	18
142 #define DCF_P3	19
143 
144 static struct partab
145 {
146 	char offset;			/* start bit of parity field */
147 } partab[] =
148 {
149 	{ 21 }, { 29 }, { 36 }, { 59 }
150 };
151 
152 #define DCF_P_P1	0
153 #define DCF_P_P2	1
154 #define DCF_P_P3	2
155 
156 #define DCF_Z_MET 0x2
157 #define DCF_Z_MED 0x1
158 
159 static u_long
160 ext_bf(
161 	register unsigned char *buf,
162 	register int   idx,
163 	register unsigned char *zero
164 	)
165 {
166 	register u_long sum = 0;
167 	register int i, first;
168 
169 	first = rawdcfcode[idx].offset;
170 
171 	for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
172 	{
173 		sum <<= 1;
174 		sum |= (buf[i] != zero[i]);
175 	}
176 	return sum;
177 }
178 
179 static unsigned
180 pcheck(
181        unsigned char *buf,
182        int   idx,
183        unsigned char *zero
184        )
185 {
186 	int i,last;
187 	unsigned psum = 1;
188 
189 	last = partab[idx+1].offset;
190 
191 	for (i = partab[idx].offset; i < last; i++)
192 	    psum ^= (buf[i] != zero[i]);
193 
194 	return psum;
195 }
196 
197 static u_long
198 convert_rawdcf(
199 	       unsigned char   *buffer,
200 	       int              size,
201 	       struct dcfparam *dcfprm,
202 	       clocktime_t     *clock_time
203 	       )
204 {
205 	register unsigned char *s = buffer;
206 	register unsigned char *b = dcfprm->onebits;
207 	register unsigned char *c = dcfprm->zerobits;
208 	register int i;
209 
210 	parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%s\"\n", buffer));
211 
212 	if (size < 57)
213 	{
214 #ifndef PARSEKERNEL
215 		msyslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size);
216 #endif
217 		return CVT_NONE;
218 	}
219 
220 	for (i = 0; i < 58; i++)
221 	{
222 		if ((*s != *b) && (*s != *c))
223 		{
224 			/*
225 			 * we only have two types of bytes (ones and zeros)
226 			 */
227 #ifndef PARSEKERNEL
228 			msyslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer);
229 #endif
230 			return CVT_NONE;
231 		}
232 		b++;
233 		c++;
234 		s++;
235 	}
236 
237 	/*
238 	 * check Start and Parity bits
239 	 */
240 	if ((ext_bf(buffer, DCF_S, dcfprm->zerobits) == 1) &&
241 	    pcheck(buffer, DCF_P_P1, dcfprm->zerobits) &&
242 	    pcheck(buffer, DCF_P_P2, dcfprm->zerobits) &&
243 	    pcheck(buffer, DCF_P_P3, dcfprm->zerobits))
244 	{
245 		/*
246 		 * buffer OK
247 		 */
248 		parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n"));
249 
250 		clock_time->flags  = PARSEB_S_ANTENNA|PARSEB_S_LEAP;
251 		clock_time->utctime= 0;
252 		clock_time->usecond= 0;
253 		clock_time->second = 0;
254 		clock_time->minute = ext_bf(buffer, DCF_M10, dcfprm->zerobits);
255 		clock_time->minute = TIMES10(clock_time->minute) + ext_bf(buffer, DCF_M1, dcfprm->zerobits);
256 		clock_time->hour   = ext_bf(buffer, DCF_H10, dcfprm->zerobits);
257 		clock_time->hour   = TIMES10(clock_time->hour) + ext_bf(buffer, DCF_H1, dcfprm->zerobits);
258 		clock_time->day    = ext_bf(buffer, DCF_D10, dcfprm->zerobits);
259 		clock_time->day    = TIMES10(clock_time->day) + ext_bf(buffer, DCF_D1, dcfprm->zerobits);
260 		clock_time->month  = ext_bf(buffer, DCF_MO0, dcfprm->zerobits);
261 		clock_time->month  = TIMES10(clock_time->month) + ext_bf(buffer, DCF_MO, dcfprm->zerobits);
262 		clock_time->year   = ext_bf(buffer, DCF_Y10, dcfprm->zerobits);
263 		clock_time->year   = TIMES10(clock_time->year) + ext_bf(buffer, DCF_Y1, dcfprm->zerobits);
264 
265 		switch (ext_bf(buffer, DCF_Z, dcfprm->zerobits))
266 		{
267 		    case DCF_Z_MET:
268 			clock_time->utcoffset = -1*60*60;
269 			break;
270 
271 		    case DCF_Z_MED:
272 			clock_time->flags     |= PARSEB_DST;
273 			clock_time->utcoffset  = -2*60*60;
274 			break;
275 
276 		    default:
277 			parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n"));
278 			return CVT_FAIL|CVT_BADFMT;
279 		}
280 
281 		if (ext_bf(buffer, DCF_A1, dcfprm->zerobits))
282 		    clock_time->flags |= PARSEB_ANNOUNCE;
283 
284 		if (ext_bf(buffer, DCF_A2, dcfprm->zerobits))
285 		    clock_time->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
286 
287 		if (ext_bf(buffer, DCF_R, dcfprm->zerobits))
288 		    clock_time->flags |= PARSEB_ALTERNATE;
289 
290 		parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%lx\n",
291 				       (int)clock_time->hour, (int)clock_time->minute, (int)clock_time->day, (int)clock_time->month,(int) clock_time->year,
292 				       (u_long)clock_time->flags));
293 		return CVT_OK;
294 	}
295 	else
296 	{
297 		/*
298 		 * bad format - not for us
299 		 */
300 #ifndef PARSEKERNEL
301 		msyslog(LOG_ERR, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer);
302 #endif
303 		return CVT_FAIL|CVT_BADFMT;
304 	}
305 }
306 
307 /*
308  * raw dcf input routine - needs to fix up 50 baud
309  * characters for 1/0 decision
310  */
311 static u_long
312 cvt_rawdcf(
313 	   unsigned char   *buffer,
314 	   int              size,
315 	   struct format   *param,
316 	   clocktime_t     *clock_time,
317 	   void            *local
318 	   )
319 {
320 	         last_tcode_t  *t = (last_tcode_t *)local;
321 	register unsigned char *s = (unsigned char *)buffer;
322 	register unsigned char *e = s + size;
323 	register unsigned char *b = dcfparameter.onebits;
324 	register unsigned char *c = dcfparameter.zerobits;
325 	         u_long   rtc = CVT_NONE;
326 	register unsigned int i, lowmax, highmax, cutoff, span;
327 #define BITS 9
328 	unsigned char     histbuf[BITS];
329 	/*
330 	 * the input buffer contains characters with runs of consecutive
331 	 * bits set. These set bits are an indication of the DCF77 pulse
332 	 * length. We assume that we receive the pulse at 50 Baud. Thus
333 	 * a 100ms pulse would generate a 4 bit train (20ms per bit and
334 	 * start bit)
335 	 * a 200ms pulse would create all zeroes (and probably a frame error)
336 	 */
337 
338 	for (i = 0; i < BITS; i++)
339 	{
340 		histbuf[i] = 0;
341 	}
342 
343 	cutoff = 0;
344 	lowmax = 0;
345 
346 	while (s < e)
347 	{
348 		register unsigned int ch = *s ^ 0xFF;
349 		/*
350 		 * these lines are left as an excercise to the reader 8-)
351 		 */
352 		if (!((ch+1) & ch) || !*s)
353 		{
354 
355 			for (i = 0; ch; i++)
356 			{
357 				ch >>= 1;
358 			}
359 
360 			*s = i;
361 			histbuf[i]++;
362 			cutoff += i;
363 			lowmax++;
364 		}
365 		else
366 		{
367 			parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, (int)(s - (unsigned char *)buffer)));
368 			*s = (unsigned char)~0;
369 			rtc = CVT_FAIL|CVT_BADFMT;
370 		}
371 		s++;
372 	}
373 
374 	if (lowmax)
375 	{
376 		cutoff /= lowmax;
377 	}
378 	else
379 	{
380 		cutoff = 4;	/* doesn't really matter - it'll fail anyway, but gives error output */
381 	}
382 
383 	parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff));
384 
385 	lowmax = 0;
386 	highmax = 0;
387 
388 	parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:"));
389 	for (i = 0; i <= cutoff; i++)
390 	{
391 		lowmax+=histbuf[i] * i;
392 		highmax += histbuf[i];
393 		parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
394 	}
395 	parseprintf(DD_RAWDCF, (" <M>"));
396 
397 	lowmax += highmax / 2;
398 
399 	if (highmax)
400 	{
401 		lowmax /= highmax;
402 	}
403 	else
404 	{
405 		lowmax = 0;
406 	}
407 
408 	highmax = 0;
409 	cutoff = 0;
410 
411 	for (; i < BITS; i++)
412 	{
413 		highmax+=histbuf[i] * i;
414 		cutoff +=histbuf[i];
415 		parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
416 	}
417 	parseprintf(DD_RAWDCF,("\n"));
418 
419 	if (cutoff)
420 	{
421 		highmax /= cutoff;
422 	}
423 	else
424 	{
425 		highmax = BITS-1;
426 	}
427 
428 	span = cutoff = lowmax;
429 	for (i = lowmax; i <= highmax; i++)
430 	{
431 		if (histbuf[cutoff] > histbuf[i])
432 		{
433 			cutoff = i;
434 			span = i;
435 		}
436 		else
437 		    if (histbuf[cutoff] == histbuf[i])
438 		    {
439 			    span = i;
440 		    }
441 	}
442 
443 	cutoff = (cutoff + span) / 2;
444 
445 	parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff));
446 
447 	s = (unsigned char *)buffer;
448 	while ((s < e) && *c && *b)
449 	{
450 		if (*s == (unsigned char)~0)
451 		{
452 			*s = '?';
453 		}
454 		else
455 		{
456 			*s = (*s >= cutoff) ? *b : *c;
457 		}
458 		s++;
459 		b++;
460 		c++;
461 	}
462 
463         if (rtc == CVT_NONE)
464         {
465 	       rtc = convert_rawdcf(buffer, size, &dcfparameter, clock_time);
466 	       if (rtc == CVT_OK)
467 	       {
468 			time_t newtime;
469 
470 			newtime = parse_to_unixtime(clock_time, &rtc);
471 			if ((rtc == CVT_OK) && t)
472 			{
473 				if ((newtime - t->tcode) == 60) /* guard against multi bit errors */
474 				{
475 					clock_time->utctime = newtime;
476 				}
477 				else
478 				{
479 					rtc = CVT_FAIL|CVT_BADTIME;
480 				}
481 				t->tcode            = newtime;
482 			}
483 	       }
484         }
485 
486     	return rtc;
487 }
488 
489 /*
490  * pps_rawdcf
491  *
492  * currently a very stupid version - should be extended to decode
493  * also ones and zeros (which is easy)
494  */
495 /*ARGSUSED*/
496 static u_long
497 pps_rawdcf(
498 	register parse_t *parseio,
499 	register int status,
500 	register timestamp_t *ptime
501 	)
502 {
503 	if (!status)		/* negative edge for simpler wiring (Rx->DCD) */
504 	{
505 		parseio->parse_dtime.parse_ptime  = *ptime;
506 		parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
507 	}
508 
509 	return CVT_NONE;
510 }
511 
512 static u_long
513 snt_rawdcf(
514 	register parse_t *parseio,
515 	register timestamp_t *ptime
516 	)
517 {
518 	if ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK)
519 	{
520 		parseio->parse_dtime.parse_stime = *ptime;
521 
522 #ifdef PARSEKERNEL
523 		parseio->parse_dtime.parse_time.tv.tv_sec++;
524 #else
525 		parseio->parse_dtime.parse_time.fp.l_ui++;
526 #endif
527 
528 		parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1));
529 
530 		return updatetimeinfo(parseio, parseio->parse_lstate);
531 	}
532 	return CVT_NONE;
533 }
534 
535 /*
536  * inp_rawdcf
537  *
538  * grep DCF77 data from input stream
539  */
540 static u_long
541 inp_rawdcf(
542 	  parse_t      *parseio,
543 	  unsigned int  ch,
544 	  timestamp_t  *tstamp
545 	  )
546 {
547 	static struct timeval timeout = { 1, 500000 }; /* 1.5 secongs denote second #60 */
548 
549 	parseprintf(DD_PARSE, ("inp_rawdcf(0x%lx, 0x%x, ...)\n", (long)parseio, ch));
550 
551 	parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
552 
553 	if (parse_timedout(parseio, tstamp, &timeout))
554 	{
555 		parseprintf(DD_PARSE, ("inp_rawdcf: time out seen\n"));
556 
557 		(void) parse_end(parseio);
558 		(void) parse_addchar(parseio, ch);
559 		return PARSE_INP_TIME;
560 	}
561 	else
562 	{
563 		unsigned int rtc;
564 
565 		rtc = parse_addchar(parseio, ch);
566 		if (rtc == PARSE_INP_SKIP)
567 		{
568 			if (snt_rawdcf(parseio, tstamp) == CVT_OK)
569 				return PARSE_INP_SYNTH;
570 		}
571 		return rtc;
572 	}
573 }
574 
575 #else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */
576 int clk_rawdcf_bs;
577 #endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */
578 
579 /*
580  * History:
581  *
582  * clk_rawdcf.c,v
583  * Revision 4.9  1999/12/06 13:42:23  kardel
584  * transfer correctly converted time codes always into tcode
585  *
586  * Revision 4.8  1999/11/28 09:13:50  kardel
587  * RECON_4_0_98F
588  *
589  * Revision 4.7  1999/04/01 20:07:20  kardel
590  * added checking for minutie increment of timestamps in clk_rawdcf.c
591  *
592  * Revision 4.6  1998/06/14 21:09:37  kardel
593  * Sun acc cleanup
594  *
595  * Revision 4.5  1998/06/13 12:04:16  kardel
596  * fix SYSV clock name clash
597  *
598  * Revision 4.4  1998/06/12 15:22:28  kardel
599  * fix prototypes
600  *
601  * Revision 4.3  1998/06/06 18:33:36  kardel
602  * simplified condidional compile expression
603  *
604  * Revision 4.2  1998/05/24 11:04:18  kardel
605  * triggering PPS on negative edge for simpler wiring (Rx->DCD)
606  *
607  * Revision 4.1  1998/05/24 09:39:53  kardel
608  * implementation of the new IO handling model
609  *
610  * Revision 4.0  1998/04/10 19:45:30  kardel
611  * Start 4.0 release version numbering
612  *
613  * from V3 3.24 log info deleted 1998/04/11 kardel
614  *
615  */
616