1 /* $NetBSD: clk_rawdcf.c,v 1.4 2006/06/11 19:34:10 kardel Exp $ */
4 * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A
6 * clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A
8 * Raw DCF77 pulse clock support
10 * Copyright (c) 1995-2006 by Frank Kardel <kardel <AT> ntp.org>
11 * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universität Erlangen-Nürnberg, Germany
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. Neither the name of the author nor the names of its contributors
22 * may be used to endorse or promote products derived from this software
23 * without specific prior written permission.
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
26 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
27 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
31 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
43 #if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_RAWDCF)
46 #include "ntp_unixtime.h"
47 #include "ntp_calendar.h"
51 # include <sys/parsestreams.h>
55 # include "ntp_stdlib.h"
61 * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
62 * und Berlin, Maerz 1989
64 * Timecode transmission:
66 * time marks are send every second except for the second before the
68 * time marks consist of a reduction of transmitter power to 25%
69 * of the nominal level
70 * the falling edge is the time indication (on time)
71 * time marks of a 100ms duration constitute a logical 0
72 * time marks of a 200ms duration constitute a logical 1
74 * see the spec. (basically a (non-)inverted psuedo random phase shift)
78 * 0 - 10 AM: free, FM: 0
80 * 15 R - alternate antenna
81 * 16 A1 - expect zone change (1 hour before)
82 * 17 - 18 Z1,Z2 - time zone
85 * 1 0 MESZ (MED, MET DST)
87 * 19 A2 - expect leap insertion/deletion (1 hour before)
88 * 20 S - start of time code (1)
89 * 21 - 24 M1 - BCD (lsb first) Minutes
90 * 25 - 27 M10 - BCD (lsb first) 10 Minutes
91 * 28 P1 - Minute Parity (even)
92 * 29 - 32 H1 - BCD (lsb first) Hours
93 * 33 - 34 H10 - BCD (lsb first) 10 Hours
94 * 35 P2 - Hour Parity (even)
95 * 36 - 39 D1 - BCD (lsb first) Days
96 * 40 - 41 D10 - BCD (lsb first) 10 Days
97 * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
98 * 45 - 49 MO - BCD (lsb first) Month
100 * 51 - 53 Y1 - BCD (lsb first) Years
101 * 54 - 57 Y10 - BCD (lsb first) 10 Years
102 * 58 P3 - Date Parity (even)
103 * 59 - usually missing (minute indication), except for leap insertion
106 static u_long pps_rawdcf
P((parse_t
*, int, timestamp_t
*));
107 static u_long cvt_rawdcf
P((unsigned char *, int, struct format
*, clocktime_t
*, void *));
108 static u_long inp_rawdcf
P((parse_t
*, unsigned int, timestamp_t
*));
110 typedef struct last_tcode
{
111 time_t tcode
; /* last converted time code */
114 #define BUFFER_MAX 61
116 clockformat_t clock_rawdcf
=
118 inp_rawdcf
, /* DCF77 input handling */
119 cvt_rawdcf
, /* raw dcf input conversion */
120 pps_rawdcf
, /* examining PPS information */
121 0, /* no private configuration data */
122 "RAW DCF77 Timecode", /* direct decoding / time synthesis */
124 BUFFER_MAX
, /* bit buffer */
128 static struct dcfparam
130 unsigned char *onebits
;
131 unsigned char *zerobits
;
134 (unsigned char *)"###############RADMLS1248124P124812P1248121241248112481248P??", /* 'ONE' representation */
135 (unsigned char *)"--------------------s-------p------p----------------------p__" /* 'ZERO' representation */
138 static struct rawdcfcode
140 char offset
; /* start bit */
143 { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
144 { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
170 char offset
; /* start bit of parity field */
173 { 21 }, { 29 }, { 36 }, { 59 }
180 #define DCF_Z_MET 0x2
181 #define DCF_Z_MED 0x1
193 first
= rawdcfcode
[idx
].offset
;
195 for (i
= rawdcfcode
[idx
+1].offset
- 1; i
>= first
; i
--)
198 sum
|= (buf
[i
] != zero
[i
]);
213 last
= partab
[idx
+1].offset
;
215 for (i
= partab
[idx
].offset
; i
< last
; i
++)
216 psum
^= (buf
[i
] != zero
[i
]);
223 unsigned char *buffer
,
225 struct dcfparam
*dcfprm
,
226 clocktime_t
*clock_time
229 unsigned char *s
= buffer
;
230 unsigned char *b
= dcfprm
->onebits
;
231 unsigned char *c
= dcfprm
->zerobits
;
234 parseprintf(DD_RAWDCF
,("parse: convert_rawdcf: \"%s\"\n", buffer
));
239 msyslog(LOG_ERR
, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size
);
244 for (i
= 0; i
< size
; i
++)
246 if ((*s
!= *b
) && (*s
!= *c
))
249 * we only have two types of bytes (ones and zeros)
252 msyslog(LOG_ERR
, "parse: convert_rawdcf: BAD DATA - no conversion");
262 * check Start and Parity bits
264 if ((ext_bf(buffer
, DCF_S
, dcfprm
->zerobits
) == 1) &&
265 pcheck(buffer
, DCF_P_P1
, dcfprm
->zerobits
) &&
266 pcheck(buffer
, DCF_P_P2
, dcfprm
->zerobits
) &&
267 pcheck(buffer
, DCF_P_P3
, dcfprm
->zerobits
))
272 parseprintf(DD_RAWDCF
,("parse: convert_rawdcf: parity check passed\n"));
274 clock_time
->flags
= PARSEB_S_ANTENNA
|PARSEB_S_LEAP
;
275 clock_time
->utctime
= 0;
276 clock_time
->usecond
= 0;
277 clock_time
->second
= 0;
278 clock_time
->minute
= ext_bf(buffer
, DCF_M10
, dcfprm
->zerobits
);
279 clock_time
->minute
= TIMES10(clock_time
->minute
) + ext_bf(buffer
, DCF_M1
, dcfprm
->zerobits
);
280 clock_time
->hour
= ext_bf(buffer
, DCF_H10
, dcfprm
->zerobits
);
281 clock_time
->hour
= TIMES10(clock_time
->hour
) + ext_bf(buffer
, DCF_H1
, dcfprm
->zerobits
);
282 clock_time
->day
= ext_bf(buffer
, DCF_D10
, dcfprm
->zerobits
);
283 clock_time
->day
= TIMES10(clock_time
->day
) + ext_bf(buffer
, DCF_D1
, dcfprm
->zerobits
);
284 clock_time
->month
= ext_bf(buffer
, DCF_MO0
, dcfprm
->zerobits
);
285 clock_time
->month
= TIMES10(clock_time
->month
) + ext_bf(buffer
, DCF_MO
, dcfprm
->zerobits
);
286 clock_time
->year
= ext_bf(buffer
, DCF_Y10
, dcfprm
->zerobits
);
287 clock_time
->year
= TIMES10(clock_time
->year
) + ext_bf(buffer
, DCF_Y1
, dcfprm
->zerobits
);
289 switch (ext_bf(buffer
, DCF_Z
, dcfprm
->zerobits
))
292 clock_time
->utcoffset
= -1*60*60;
296 clock_time
->flags
|= PARSEB_DST
;
297 clock_time
->utcoffset
= -2*60*60;
301 parseprintf(DD_RAWDCF
,("parse: convert_rawdcf: BAD TIME ZONE\n"));
302 return CVT_FAIL
|CVT_BADFMT
;
305 if (ext_bf(buffer
, DCF_A1
, dcfprm
->zerobits
))
306 clock_time
->flags
|= PARSEB_ANNOUNCE
;
308 if (ext_bf(buffer
, DCF_A2
, dcfprm
->zerobits
))
309 clock_time
->flags
|= PARSEB_LEAPADD
; /* default: DCF77 data format deficiency */
311 if (ext_bf(buffer
, DCF_R
, dcfprm
->zerobits
))
312 clock_time
->flags
|= PARSEB_ALTERNATE
;
314 parseprintf(DD_RAWDCF
,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%lx\n",
315 (int)clock_time
->hour
, (int)clock_time
->minute
, (int)clock_time
->day
, (int)clock_time
->month
,(int) clock_time
->year
,
316 (u_long
)clock_time
->flags
));
322 * bad format - not for us
325 msyslog(LOG_ERR
, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer
);
327 return CVT_FAIL
|CVT_BADFMT
;
332 * raw dcf input routine - needs to fix up 50 baud
333 * characters for 1/0 decision
337 unsigned char *buffer
,
339 struct format
*param
,
340 clocktime_t
*clock_time
,
344 last_tcode_t
*t
= (last_tcode_t
*)local
;
345 unsigned char *s
= (unsigned char *)buffer
;
346 unsigned char *e
= s
+ size
;
347 unsigned char *b
= dcfparameter
.onebits
;
348 unsigned char *c
= dcfparameter
.zerobits
;
349 u_long rtc
= CVT_NONE
;
350 unsigned int i
, lowmax
, highmax
, cutoff
, span
;
352 unsigned char histbuf
[BITS
];
354 * the input buffer contains characters with runs of consecutive
355 * bits set. These set bits are an indication of the DCF77 pulse
356 * length. We assume that we receive the pulse at 50 Baud. Thus
357 * a 100ms pulse would generate a 4 bit train (20ms per bit and
359 * a 200ms pulse would create all zeroes (and probably a frame error)
362 for (i
= 0; i
< BITS
; i
++)
372 unsigned int ch
= *s
^ 0xFF;
374 * these lines are left as an excercise to the reader 8-)
376 if (!((ch
+1) & ch
) || !*s
)
391 parseprintf(DD_RAWDCF
,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s
, (int)(s
- (unsigned char *)buffer
)));
392 *s
= (unsigned char)~0;
393 rtc
= CVT_FAIL
|CVT_BADFMT
;
404 cutoff
= 4; /* doesn't really matter - it'll fail anyway, but gives error output */
407 parseprintf(DD_RAWDCF
,("parse: cvt_rawdcf: average bit count: %d\n", cutoff
));
412 parseprintf(DD_RAWDCF
,("parse: cvt_rawdcf: histogram:"));
413 for (i
= 0; i
<= cutoff
; i
++)
415 lowmax
+=histbuf
[i
] * i
;
416 highmax
+= histbuf
[i
];
417 parseprintf(DD_RAWDCF
,(" %d", histbuf
[i
]));
419 parseprintf(DD_RAWDCF
, (" <M>"));
421 lowmax
+= highmax
/ 2;
435 for (; i
< BITS
; i
++)
437 highmax
+=histbuf
[i
] * i
;
439 parseprintf(DD_RAWDCF
,(" %d", histbuf
[i
]));
441 parseprintf(DD_RAWDCF
,("\n"));
452 span
= cutoff
= lowmax
;
453 for (i
= lowmax
; i
<= highmax
; i
++)
455 if (histbuf
[cutoff
] > histbuf
[i
])
461 if (histbuf
[cutoff
] == histbuf
[i
])
467 cutoff
= (cutoff
+ span
) / 2;
469 parseprintf(DD_RAWDCF
,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax
, highmax
, cutoff
));
471 s
= (unsigned char *)buffer
;
474 if (*s
== (unsigned char)~0)
480 *s
= (*s
>= cutoff
) ? *b
: *c
;
489 rtc
= convert_rawdcf(buffer
, size
, &dcfparameter
, clock_time
);
494 newtime
= parse_to_unixtime(clock_time
, &rtc
);
495 if ((rtc
== CVT_OK
) && t
)
497 if ((newtime
- t
->tcode
) == 60) /* guard against multi bit errors */
499 clock_time
->utctime
= newtime
;
503 rtc
= CVT_FAIL
|CVT_BADTIME
;
516 * currently a very stupid version - should be extended to decode
517 * also ones and zeros (which is easy)
527 if (!status
) /* negative edge for simpler wiring (Rx->DCD) */
529 parseio
->parse_dtime
.parse_ptime
= *ptime
;
530 parseio
->parse_dtime
.parse_state
|= PARSEB_PPS
|PARSEB_S_PPS
;
542 if ((parseio
->parse_dtime
.parse_status
& CVT_MASK
) == CVT_OK
)
544 parseio
->parse_dtime
.parse_stime
= *ptime
;
547 parseio
->parse_dtime
.parse_time
.tv
.tv_sec
++;
549 parseio
->parse_dtime
.parse_time
.fp
.l_ui
++;
552 parseprintf(DD_RAWDCF
,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio
->parse_index
- 1));
554 return updatetimeinfo(parseio
, parseio
->parse_lstate
);
562 * grab DCF77 data from input stream
571 static struct timeval timeout
= { 1, 500000 }; /* 1.5 secongs denote second #60 */
573 parseprintf(DD_PARSE
, ("inp_rawdcf(0x%lx, 0x%x, ...)\n", (long)parseio
, ch
));
575 parseio
->parse_dtime
.parse_stime
= *tstamp
; /* collect timestamp */
577 if (parse_timedout(parseio
, tstamp
, &timeout
))
579 parseprintf(DD_PARSE
, ("inp_rawdcf: time out seen\n"));
581 (void) parse_end(parseio
);
582 (void) parse_addchar(parseio
, ch
);
583 return PARSE_INP_TIME
;
589 rtc
= parse_addchar(parseio
, ch
);
590 if (rtc
== PARSE_INP_SKIP
)
592 if (snt_rawdcf(parseio
, tstamp
) == CVT_OK
)
593 return PARSE_INP_SYNTH
;
599 #else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */
601 #endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */
607 * Revision 4.18 2006/06/22 18:40:01 kardel
608 * clean up signedness (gcc 4)
610 * Revision 4.17 2006/01/22 16:01:55 kardel
611 * update version information
613 * Revision 4.16 2006/01/22 15:51:22 kardel
614 * generate reasonable timecode output on invalid input
616 * Revision 4.15 2005/08/06 19:17:06 kardel
619 * Revision 4.14 2005/08/06 17:39:40 kardel
620 * cleanup size handling wrt/ to buffer boundaries
622 * Revision 4.13 2005/04/16 17:32:10 kardel
625 * Revision 4.12 2004/11/14 15:29:41 kardel
626 * support PPSAPI, upgrade Copyright to Berkeley style
628 * Revision 4.9 1999/12/06 13:42:23 kardel
629 * transfer correctly converted time codes always into tcode
631 * Revision 4.8 1999/11/28 09:13:50 kardel
634 * Revision 4.7 1999/04/01 20:07:20 kardel
635 * added checking for minutie increment of timestamps in clk_rawdcf.c
637 * Revision 4.6 1998/06/14 21:09:37 kardel
640 * Revision 4.5 1998/06/13 12:04:16 kardel
641 * fix SYSV clock name clash
643 * Revision 4.4 1998/06/12 15:22:28 kardel
646 * Revision 4.3 1998/06/06 18:33:36 kardel
647 * simplified condidional compile expression
649 * Revision 4.2 1998/05/24 11:04:18 kardel
650 * triggering PPS on negative edge for simpler wiring (Rx->DCD)
652 * Revision 4.1 1998/05/24 09:39:53 kardel
653 * implementation of the new IO handling model
655 * Revision 4.0 1998/04/10 19:45:30 kardel
656 * Start 4.0 release version numbering
658 * from V3 3.24 log info deleted 1998/04/11 kardel