1*eabc0478Schristos /* $NetBSD: clk_rawdcf.c,v 1.9 2024/08/18 20:47:17 christos Exp $ */ 2abb0f93cSkardel 3abb0f93cSkardel /* 4abb0f93cSkardel * /src/NTP/REPOSITORY/ntp4-dev/libparse/clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A 5abb0f93cSkardel * 6abb0f93cSkardel * clk_rawdcf.c,v 4.18 2006/06/22 18:40:01 kardel RELEASE_20060622_A 7abb0f93cSkardel * 8abb0f93cSkardel * Raw DCF77 pulse clock support 9abb0f93cSkardel * 105d681e99Schristos * Copyright (c) 1995-2015 by Frank Kardel <kardel <AT> ntp.org> 117476e6e4Schristos * Copyright (c) 1989-1994 by Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg, Germany 12abb0f93cSkardel * 13abb0f93cSkardel * Redistribution and use in source and binary forms, with or without 14abb0f93cSkardel * modification, are permitted provided that the following conditions 15abb0f93cSkardel * are met: 16abb0f93cSkardel * 1. Redistributions of source code must retain the above copyright 17abb0f93cSkardel * notice, this list of conditions and the following disclaimer. 18abb0f93cSkardel * 2. Redistributions in binary form must reproduce the above copyright 19abb0f93cSkardel * notice, this list of conditions and the following disclaimer in the 20abb0f93cSkardel * documentation and/or other materials provided with the distribution. 21abb0f93cSkardel * 3. Neither the name of the author nor the names of its contributors 22abb0f93cSkardel * may be used to endorse or promote products derived from this software 23abb0f93cSkardel * without specific prior written permission. 24abb0f93cSkardel * 25abb0f93cSkardel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 26abb0f93cSkardel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27abb0f93cSkardel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28abb0f93cSkardel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 29abb0f93cSkardel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30abb0f93cSkardel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31abb0f93cSkardel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32abb0f93cSkardel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33abb0f93cSkardel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34abb0f93cSkardel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35abb0f93cSkardel * SUCH DAMAGE. 36abb0f93cSkardel * 37abb0f93cSkardel */ 38abb0f93cSkardel 39abb0f93cSkardel #ifdef HAVE_CONFIG_H 40abb0f93cSkardel # include <config.h> 41abb0f93cSkardel #endif 42abb0f93cSkardel 43abb0f93cSkardel #if defined(REFCLOCK) && defined(CLOCK_PARSE) && defined(CLOCK_RAWDCF) 44abb0f93cSkardel 45abb0f93cSkardel #include "ntp_fp.h" 465d681e99Schristos #include "timevalops.h" 47abb0f93cSkardel #include "ntp_unixtime.h" 48abb0f93cSkardel #include "ntp_calendar.h" 49abb0f93cSkardel 50abb0f93cSkardel #include "parse.h" 51abb0f93cSkardel #ifdef PARSESTREAM 52abb0f93cSkardel # include <sys/parsestreams.h> 53abb0f93cSkardel #endif 54abb0f93cSkardel 55abb0f93cSkardel #ifndef PARSEKERNEL 56abb0f93cSkardel # include "ntp_stdlib.h" 57abb0f93cSkardel #endif 58abb0f93cSkardel 59abb0f93cSkardel /* 60abb0f93cSkardel * DCF77 raw time code 61abb0f93cSkardel * 62abb0f93cSkardel * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig 63abb0f93cSkardel * und Berlin, Maerz 1989 64abb0f93cSkardel * 65abb0f93cSkardel * Timecode transmission: 66abb0f93cSkardel * AM: 67abb0f93cSkardel * time marks are send every second except for the second before the 68abb0f93cSkardel * next minute mark 69abb0f93cSkardel * time marks consist of a reduction of transmitter power to 25% 70abb0f93cSkardel * of the nominal level 71abb0f93cSkardel * the falling edge is the time indication (on time) 72abb0f93cSkardel * time marks of a 100ms duration constitute a logical 0 73abb0f93cSkardel * time marks of a 200ms duration constitute a logical 1 74abb0f93cSkardel * FM: 75abb0f93cSkardel * see the spec. (basically a (non-)inverted psuedo random phase shift) 76abb0f93cSkardel * 77abb0f93cSkardel * Encoding: 78abb0f93cSkardel * Second Contents 79abb0f93cSkardel * 0 - 10 AM: free, FM: 0 80abb0f93cSkardel * 11 - 14 free 817476e6e4Schristos * 15 R - "call bit" used to signalize irregularities in the control facilities 827476e6e4Schristos * (until 2003 indicated transmission via alternate antenna) 83abb0f93cSkardel * 16 A1 - expect zone change (1 hour before) 84abb0f93cSkardel * 17 - 18 Z1,Z2 - time zone 85abb0f93cSkardel * 0 0 illegal 86abb0f93cSkardel * 0 1 MEZ (MET) 87abb0f93cSkardel * 1 0 MESZ (MED, MET DST) 88abb0f93cSkardel * 1 1 illegal 89abb0f93cSkardel * 19 A2 - expect leap insertion/deletion (1 hour before) 90abb0f93cSkardel * 20 S - start of time code (1) 91abb0f93cSkardel * 21 - 24 M1 - BCD (lsb first) Minutes 92abb0f93cSkardel * 25 - 27 M10 - BCD (lsb first) 10 Minutes 93abb0f93cSkardel * 28 P1 - Minute Parity (even) 94abb0f93cSkardel * 29 - 32 H1 - BCD (lsb first) Hours 95abb0f93cSkardel * 33 - 34 H10 - BCD (lsb first) 10 Hours 96abb0f93cSkardel * 35 P2 - Hour Parity (even) 97abb0f93cSkardel * 36 - 39 D1 - BCD (lsb first) Days 98abb0f93cSkardel * 40 - 41 D10 - BCD (lsb first) 10 Days 99abb0f93cSkardel * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday) 100abb0f93cSkardel * 45 - 49 MO - BCD (lsb first) Month 101abb0f93cSkardel * 50 MO0 - 10 Months 102abb0f93cSkardel * 51 - 53 Y1 - BCD (lsb first) Years 103abb0f93cSkardel * 54 - 57 Y10 - BCD (lsb first) 10 Years 104abb0f93cSkardel * 58 P3 - Date Parity (even) 105abb0f93cSkardel * 59 - usually missing (minute indication), except for leap insertion 106abb0f93cSkardel */ 107abb0f93cSkardel 1087476e6e4Schristos static parse_pps_fnc_t pps_rawdcf; 1097476e6e4Schristos static parse_cvt_fnc_t cvt_rawdcf; 1107476e6e4Schristos static parse_inp_fnc_t inp_rawdcf; 111abb0f93cSkardel 112abb0f93cSkardel typedef struct last_tcode { 113abb0f93cSkardel time_t tcode; /* last converted time code */ 1145d681e99Schristos timestamp_t tminute; /* sample time for minute start */ 1155d681e99Schristos timestamp_t timeout; /* last timeout timestamp */ 116abb0f93cSkardel } last_tcode_t; 117abb0f93cSkardel 118abb0f93cSkardel #define BUFFER_MAX 61 119abb0f93cSkardel 120abb0f93cSkardel clockformat_t clock_rawdcf = 121abb0f93cSkardel { 122abb0f93cSkardel inp_rawdcf, /* DCF77 input handling */ 123abb0f93cSkardel cvt_rawdcf, /* raw dcf input conversion */ 124abb0f93cSkardel pps_rawdcf, /* examining PPS information */ 125abb0f93cSkardel 0, /* no private configuration data */ 126abb0f93cSkardel "RAW DCF77 Timecode", /* direct decoding / time synthesis */ 127abb0f93cSkardel 128abb0f93cSkardel BUFFER_MAX, /* bit buffer */ 129abb0f93cSkardel sizeof(last_tcode_t) 130abb0f93cSkardel }; 131abb0f93cSkardel 132abb0f93cSkardel static struct dcfparam 133abb0f93cSkardel { 134e19314b7Schristos const unsigned char *onebits; 135e19314b7Schristos const unsigned char *zerobits; 136abb0f93cSkardel } dcfparameter = 137abb0f93cSkardel { 138e19314b7Schristos (const unsigned char *)"###############RADMLS1248124P124812P1248121241248112481248P??", /* 'ONE' representation */ 139e19314b7Schristos (const unsigned char *)"--------------------s-------p------p----------------------p__" /* 'ZERO' representation */ 140abb0f93cSkardel }; 141abb0f93cSkardel 142abb0f93cSkardel static struct rawdcfcode 143abb0f93cSkardel { 144abb0f93cSkardel char offset; /* start bit */ 145abb0f93cSkardel } rawdcfcode[] = 146abb0f93cSkardel { 147abb0f93cSkardel { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 }, 148abb0f93cSkardel { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 } 149abb0f93cSkardel }; 150abb0f93cSkardel 151abb0f93cSkardel #define DCF_M 0 152abb0f93cSkardel #define DCF_R 1 153abb0f93cSkardel #define DCF_A1 2 154abb0f93cSkardel #define DCF_Z 3 155abb0f93cSkardel #define DCF_A2 4 156abb0f93cSkardel #define DCF_S 5 157abb0f93cSkardel #define DCF_M1 6 158abb0f93cSkardel #define DCF_M10 7 159abb0f93cSkardel #define DCF_P1 8 160abb0f93cSkardel #define DCF_H1 9 161abb0f93cSkardel #define DCF_H10 10 162abb0f93cSkardel #define DCF_P2 11 163abb0f93cSkardel #define DCF_D1 12 164abb0f93cSkardel #define DCF_D10 13 165abb0f93cSkardel #define DCF_DW 14 166abb0f93cSkardel #define DCF_MO 15 167abb0f93cSkardel #define DCF_MO0 16 168abb0f93cSkardel #define DCF_Y1 17 169abb0f93cSkardel #define DCF_Y10 18 170abb0f93cSkardel #define DCF_P3 19 171abb0f93cSkardel 172abb0f93cSkardel static struct partab 173abb0f93cSkardel { 174abb0f93cSkardel char offset; /* start bit of parity field */ 175abb0f93cSkardel } partab[] = 176abb0f93cSkardel { 177abb0f93cSkardel { 21 }, { 29 }, { 36 }, { 59 } 178abb0f93cSkardel }; 179abb0f93cSkardel 180abb0f93cSkardel #define DCF_P_P1 0 181abb0f93cSkardel #define DCF_P_P2 1 182abb0f93cSkardel #define DCF_P_P3 2 183abb0f93cSkardel 184abb0f93cSkardel #define DCF_Z_MET 0x2 185abb0f93cSkardel #define DCF_Z_MED 0x1 186abb0f93cSkardel 187abb0f93cSkardel static u_long 188abb0f93cSkardel ext_bf( 189abb0f93cSkardel unsigned char *buf, 190abb0f93cSkardel int idx, 191e19314b7Schristos const unsigned char *zero 192abb0f93cSkardel ) 193abb0f93cSkardel { 194abb0f93cSkardel u_long sum = 0; 195abb0f93cSkardel int i, first; 196abb0f93cSkardel 197abb0f93cSkardel first = rawdcfcode[idx].offset; 198abb0f93cSkardel 199abb0f93cSkardel for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--) 200abb0f93cSkardel { 201abb0f93cSkardel sum <<= 1; 202abb0f93cSkardel sum |= (buf[i] != zero[i]); 203abb0f93cSkardel } 204abb0f93cSkardel return sum; 205abb0f93cSkardel } 206abb0f93cSkardel 207abb0f93cSkardel static unsigned 208abb0f93cSkardel pcheck( 209abb0f93cSkardel unsigned char *buf, 210abb0f93cSkardel int idx, 211e19314b7Schristos const unsigned char *zero 212abb0f93cSkardel ) 213abb0f93cSkardel { 214abb0f93cSkardel int i,last; 215abb0f93cSkardel unsigned psum = 1; 216abb0f93cSkardel 217abb0f93cSkardel last = partab[idx+1].offset; 218abb0f93cSkardel 219abb0f93cSkardel for (i = partab[idx].offset; i < last; i++) 220abb0f93cSkardel psum ^= (buf[i] != zero[i]); 221abb0f93cSkardel 222abb0f93cSkardel return psum; 223abb0f93cSkardel } 224abb0f93cSkardel 225cdfa2a7eSchristos static int/*BOOL*/ 226cdfa2a7eSchristos zeller_expand( 227cdfa2a7eSchristos clocktime_t *clock_time, 228cdfa2a7eSchristos unsigned int wd 229cdfa2a7eSchristos ) 230cdfa2a7eSchristos { 231cdfa2a7eSchristos unsigned int y = (unsigned int)clock_time->year; 232cdfa2a7eSchristos unsigned int m = (unsigned int)clock_time->month - 1u; 233cdfa2a7eSchristos unsigned int d = (unsigned int)clock_time->day - 1u; 234cdfa2a7eSchristos unsigned int c; 235cdfa2a7eSchristos 236cdfa2a7eSchristos /* Check basic constraints first. */ 237cdfa2a7eSchristos if ((y >= 100u) || (m >= 12u) || (d >= 31u) || (--wd >= 7u)) 238cdfa2a7eSchristos return FALSE; 239cdfa2a7eSchristos 240cdfa2a7eSchristos /* Get weekday of date in 1st century by a variation on Zeller's 241cdfa2a7eSchristos * congruence. All operands are non-negative, and the month 242cdfa2a7eSchristos * formula is adjusted to use a divider of 32, so we can do a 243cdfa2a7eSchristos * shift instead of a 'true' division: 244cdfa2a7eSchristos */ 245cdfa2a7eSchristos if ((m += 10u) >= 12u) /* shift base to 0000-03-01 */ 246cdfa2a7eSchristos m -= 12u; 247cdfa2a7eSchristos else if (--y >= 100u) 248cdfa2a7eSchristos y += 100; 249cdfa2a7eSchristos d += y + (y >> 2) + 2u; /* year-related share */ 250cdfa2a7eSchristos d += (m * 83u + 16u) >> 5; /* month-related share */ 251cdfa2a7eSchristos 252cdfa2a7eSchristos /* The next step combines the exact division by modular inverse 253cdfa2a7eSchristos * with the (mod 7) step in such way that no true division and 254cdfa2a7eSchristos * only one multiplication is needed. The multiplier is 255cdfa2a7eSchristos * M <- ceil((3*8)/7 * 2**29) 256cdfa2a7eSchristos * and combines multiplication by invmod(5, 7) -> 3 and modulus 257cdfa2a7eSchristos * by 7 transformation to (mod 8) in one step. 258cdfa2a7eSchristos * Note that 252 == 0 (mod 7) and that 'd' is less than 185, 259cdfa2a7eSchristos * so the number to invert and reduce is strictly positive. In 260cdfa2a7eSchristos * the end, 'c' is number of centuries since start of a great 261cdfa2a7eSchristos * cycle and must be in [0..3] or we had bad input. 262cdfa2a7eSchristos */ 263cdfa2a7eSchristos c = (((252u + wd - d) * 0x6db6db6eU) >> 29) & 7u; 264cdfa2a7eSchristos if (c >= 4) 265cdfa2a7eSchristos return FALSE; 266cdfa2a7eSchristos /* undo calendar base shift now */ 267cdfa2a7eSchristos if ((m > 9u) && (++y >= 100u)) { 268cdfa2a7eSchristos y -= 100u; 269cdfa2a7eSchristos c = (c + 1u) & 3u; 270cdfa2a7eSchristos } 271cdfa2a7eSchristos /* combine year with centuries & map to [1970..2369] */ 272cdfa2a7eSchristos y += (c * 100u); 273cdfa2a7eSchristos clock_time->year = (int)y + ((y < 370u) ? 2000 : 1600); 274cdfa2a7eSchristos return TRUE; 275cdfa2a7eSchristos } 276cdfa2a7eSchristos 277abb0f93cSkardel static u_long 278abb0f93cSkardel convert_rawdcf( 279abb0f93cSkardel unsigned char *buffer, 280abb0f93cSkardel int size, 281abb0f93cSkardel struct dcfparam *dcfprm, 282abb0f93cSkardel clocktime_t *clock_time 283abb0f93cSkardel ) 284abb0f93cSkardel { 285abb0f93cSkardel unsigned char *s = buffer; 286e19314b7Schristos const unsigned char *b = dcfprm->onebits; 287e19314b7Schristos const unsigned char *c = dcfprm->zerobits; 288abb0f93cSkardel int i; 289abb0f93cSkardel 2905d681e99Schristos parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%.*s\"\n", size, buffer)); 291abb0f93cSkardel 292abb0f93cSkardel if (size < 57) 293abb0f93cSkardel { 294abb0f93cSkardel #ifndef PARSEKERNEL 2952950cc38Schristos msyslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits", size); 296abb0f93cSkardel #endif 2975d681e99Schristos return CVT_FAIL|CVT_BADFMT; 298abb0f93cSkardel } 299abb0f93cSkardel 300abb0f93cSkardel for (i = 0; i < size; i++) 301abb0f93cSkardel { 302abb0f93cSkardel if ((*s != *b) && (*s != *c)) 303abb0f93cSkardel { 304abb0f93cSkardel /* 305abb0f93cSkardel * we only have two types of bytes (ones and zeros) 306abb0f93cSkardel */ 307abb0f93cSkardel #ifndef PARSEKERNEL 308abb0f93cSkardel msyslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion"); 309abb0f93cSkardel #endif 3105d681e99Schristos return CVT_FAIL|CVT_BADFMT; 311abb0f93cSkardel } 312abb0f93cSkardel if (*b) b++; 313abb0f93cSkardel if (*c) c++; 314abb0f93cSkardel s++; 315abb0f93cSkardel } 316abb0f93cSkardel 317abb0f93cSkardel /* 318abb0f93cSkardel * check Start and Parity bits 319abb0f93cSkardel */ 320abb0f93cSkardel if ((ext_bf(buffer, DCF_S, dcfprm->zerobits) == 1) && 321abb0f93cSkardel pcheck(buffer, DCF_P_P1, dcfprm->zerobits) && 322abb0f93cSkardel pcheck(buffer, DCF_P_P2, dcfprm->zerobits) && 323abb0f93cSkardel pcheck(buffer, DCF_P_P3, dcfprm->zerobits)) 324abb0f93cSkardel { 325abb0f93cSkardel /* 326abb0f93cSkardel * buffer OK 327abb0f93cSkardel */ 328abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n")); 329abb0f93cSkardel 3305d681e99Schristos clock_time->flags = PARSEB_S_CALLBIT|PARSEB_S_LEAP; 331abb0f93cSkardel clock_time->utctime= 0; 332abb0f93cSkardel clock_time->usecond= 0; 333abb0f93cSkardel clock_time->second = 0; 334abb0f93cSkardel clock_time->minute = ext_bf(buffer, DCF_M10, dcfprm->zerobits); 335abb0f93cSkardel clock_time->minute = TIMES10(clock_time->minute) + ext_bf(buffer, DCF_M1, dcfprm->zerobits); 336abb0f93cSkardel clock_time->hour = ext_bf(buffer, DCF_H10, dcfprm->zerobits); 337abb0f93cSkardel clock_time->hour = TIMES10(clock_time->hour) + ext_bf(buffer, DCF_H1, dcfprm->zerobits); 338abb0f93cSkardel clock_time->day = ext_bf(buffer, DCF_D10, dcfprm->zerobits); 339abb0f93cSkardel clock_time->day = TIMES10(clock_time->day) + ext_bf(buffer, DCF_D1, dcfprm->zerobits); 340abb0f93cSkardel clock_time->month = ext_bf(buffer, DCF_MO0, dcfprm->zerobits); 341abb0f93cSkardel clock_time->month = TIMES10(clock_time->month) + ext_bf(buffer, DCF_MO, dcfprm->zerobits); 342abb0f93cSkardel clock_time->year = ext_bf(buffer, DCF_Y10, dcfprm->zerobits); 343abb0f93cSkardel clock_time->year = TIMES10(clock_time->year) + ext_bf(buffer, DCF_Y1, dcfprm->zerobits); 344abb0f93cSkardel 345cdfa2a7eSchristos if (!zeller_expand(clock_time, ext_bf(buffer, DCF_DW, dcfprm->zerobits))) 346cdfa2a7eSchristos return CVT_FAIL|CVT_BADFMT; 347cdfa2a7eSchristos 348abb0f93cSkardel switch (ext_bf(buffer, DCF_Z, dcfprm->zerobits)) 349abb0f93cSkardel { 350abb0f93cSkardel case DCF_Z_MET: 351abb0f93cSkardel clock_time->utcoffset = -1*60*60; 352abb0f93cSkardel break; 353abb0f93cSkardel 354abb0f93cSkardel case DCF_Z_MED: 355abb0f93cSkardel clock_time->flags |= PARSEB_DST; 356abb0f93cSkardel clock_time->utcoffset = -2*60*60; 357abb0f93cSkardel break; 358abb0f93cSkardel 359abb0f93cSkardel default: 360abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n")); 361abb0f93cSkardel return CVT_FAIL|CVT_BADFMT; 362abb0f93cSkardel } 363abb0f93cSkardel 364abb0f93cSkardel if (ext_bf(buffer, DCF_A1, dcfprm->zerobits)) 365abb0f93cSkardel clock_time->flags |= PARSEB_ANNOUNCE; 366abb0f93cSkardel 367abb0f93cSkardel if (ext_bf(buffer, DCF_A2, dcfprm->zerobits)) 368abb0f93cSkardel clock_time->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */ 369abb0f93cSkardel 370abb0f93cSkardel if (ext_bf(buffer, DCF_R, dcfprm->zerobits)) 3717476e6e4Schristos clock_time->flags |= PARSEB_CALLBIT; 372abb0f93cSkardel 3735d681e99Schristos parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %02d:%02d, %02d.%02d.%02d, flags 0x%lx\n", 374abb0f93cSkardel (int)clock_time->hour, (int)clock_time->minute, (int)clock_time->day, (int)clock_time->month,(int) clock_time->year, 375abb0f93cSkardel (u_long)clock_time->flags)); 376abb0f93cSkardel return CVT_OK; 377abb0f93cSkardel } 378abb0f93cSkardel else 379abb0f93cSkardel { 380abb0f93cSkardel /* 381abb0f93cSkardel * bad format - not for us 382abb0f93cSkardel */ 383abb0f93cSkardel #ifndef PARSEKERNEL 3845d681e99Schristos msyslog(LOG_ERR, "parse: convert_rawdcf: start bit / parity check FAILED for \"%.*s\"", size, buffer); 385abb0f93cSkardel #endif 386abb0f93cSkardel return CVT_FAIL|CVT_BADFMT; 387abb0f93cSkardel } 388abb0f93cSkardel } 389abb0f93cSkardel 390abb0f93cSkardel /* 3917476e6e4Schristos * parse_cvt_fnc_t cvt_rawdcf 392abb0f93cSkardel * raw dcf input routine - needs to fix up 50 baud 393abb0f93cSkardel * characters for 1/0 decision 394abb0f93cSkardel */ 395abb0f93cSkardel static u_long 396abb0f93cSkardel cvt_rawdcf( 397abb0f93cSkardel unsigned char *buffer, 398abb0f93cSkardel int size, 399abb0f93cSkardel struct format *param, 400abb0f93cSkardel clocktime_t *clock_time, 401abb0f93cSkardel void *local 402abb0f93cSkardel ) 403abb0f93cSkardel { 404abb0f93cSkardel last_tcode_t *t = (last_tcode_t *)local; 405abb0f93cSkardel unsigned char *s = (unsigned char *)buffer; 406abb0f93cSkardel unsigned char *e = s + size; 407e19314b7Schristos const unsigned char *b = dcfparameter.onebits; 408e19314b7Schristos const unsigned char *c = dcfparameter.zerobits; 409abb0f93cSkardel u_long rtc = CVT_NONE; 410abb0f93cSkardel unsigned int i, lowmax, highmax, cutoff, span; 411abb0f93cSkardel #define BITS 9 412abb0f93cSkardel unsigned char histbuf[BITS]; 413abb0f93cSkardel /* 414abb0f93cSkardel * the input buffer contains characters with runs of consecutive 415abb0f93cSkardel * bits set. These set bits are an indication of the DCF77 pulse 416abb0f93cSkardel * length. We assume that we receive the pulse at 50 Baud. Thus 417abb0f93cSkardel * a 100ms pulse would generate a 4 bit train (20ms per bit and 418abb0f93cSkardel * start bit) 419abb0f93cSkardel * a 200ms pulse would create all zeroes (and probably a frame error) 420abb0f93cSkardel */ 421abb0f93cSkardel 422abb0f93cSkardel for (i = 0; i < BITS; i++) 423abb0f93cSkardel { 424abb0f93cSkardel histbuf[i] = 0; 425abb0f93cSkardel } 426abb0f93cSkardel 427abb0f93cSkardel cutoff = 0; 428abb0f93cSkardel lowmax = 0; 429abb0f93cSkardel 430abb0f93cSkardel while (s < e) 431abb0f93cSkardel { 432abb0f93cSkardel unsigned int ch = *s ^ 0xFF; 433abb0f93cSkardel /* 434abb0f93cSkardel * these lines are left as an excercise to the reader 8-) 435abb0f93cSkardel */ 436abb0f93cSkardel if (!((ch+1) & ch) || !*s) 437abb0f93cSkardel { 438abb0f93cSkardel 439abb0f93cSkardel for (i = 0; ch; i++) 440abb0f93cSkardel { 441abb0f93cSkardel ch >>= 1; 442abb0f93cSkardel } 443abb0f93cSkardel 4447476e6e4Schristos *s = (unsigned char) i; 445abb0f93cSkardel histbuf[i]++; 446abb0f93cSkardel cutoff += i; 447abb0f93cSkardel lowmax++; 448abb0f93cSkardel } 449abb0f93cSkardel else 450abb0f93cSkardel { 451abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, (int)(s - (unsigned char *)buffer))); 452abb0f93cSkardel *s = (unsigned char)~0; 453abb0f93cSkardel rtc = CVT_FAIL|CVT_BADFMT; 454abb0f93cSkardel } 455abb0f93cSkardel s++; 456abb0f93cSkardel } 457abb0f93cSkardel 458abb0f93cSkardel if (lowmax) 459abb0f93cSkardel { 460abb0f93cSkardel cutoff /= lowmax; 461abb0f93cSkardel } 462abb0f93cSkardel else 463abb0f93cSkardel { 464abb0f93cSkardel cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */ 465abb0f93cSkardel } 466abb0f93cSkardel 467abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff)); 468abb0f93cSkardel 469abb0f93cSkardel lowmax = 0; 470abb0f93cSkardel highmax = 0; 471abb0f93cSkardel 472abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:")); 473abb0f93cSkardel for (i = 0; i <= cutoff; i++) 474abb0f93cSkardel { 475abb0f93cSkardel lowmax+=histbuf[i] * i; 476abb0f93cSkardel highmax += histbuf[i]; 477abb0f93cSkardel parseprintf(DD_RAWDCF,(" %d", histbuf[i])); 478abb0f93cSkardel } 479abb0f93cSkardel parseprintf(DD_RAWDCF, (" <M>")); 480abb0f93cSkardel 481abb0f93cSkardel lowmax += highmax / 2; 482abb0f93cSkardel 483abb0f93cSkardel if (highmax) 484abb0f93cSkardel { 485abb0f93cSkardel lowmax /= highmax; 486abb0f93cSkardel } 487abb0f93cSkardel else 488abb0f93cSkardel { 489abb0f93cSkardel lowmax = 0; 490abb0f93cSkardel } 491abb0f93cSkardel 492abb0f93cSkardel highmax = 0; 493abb0f93cSkardel cutoff = 0; 494abb0f93cSkardel 495abb0f93cSkardel for (; i < BITS; i++) 496abb0f93cSkardel { 497abb0f93cSkardel highmax+=histbuf[i] * i; 498abb0f93cSkardel cutoff +=histbuf[i]; 499abb0f93cSkardel parseprintf(DD_RAWDCF,(" %d", histbuf[i])); 500abb0f93cSkardel } 501abb0f93cSkardel parseprintf(DD_RAWDCF,("\n")); 502abb0f93cSkardel 503abb0f93cSkardel if (cutoff) 504abb0f93cSkardel { 505abb0f93cSkardel highmax /= cutoff; 506abb0f93cSkardel } 507abb0f93cSkardel else 508abb0f93cSkardel { 509abb0f93cSkardel highmax = BITS-1; 510abb0f93cSkardel } 511abb0f93cSkardel 512abb0f93cSkardel span = cutoff = lowmax; 513abb0f93cSkardel for (i = lowmax; i <= highmax; i++) 514abb0f93cSkardel { 515abb0f93cSkardel if (histbuf[cutoff] > histbuf[i]) 516abb0f93cSkardel { 517abb0f93cSkardel cutoff = i; 518abb0f93cSkardel span = i; 519abb0f93cSkardel } 520abb0f93cSkardel else 521abb0f93cSkardel if (histbuf[cutoff] == histbuf[i]) 522abb0f93cSkardel { 523abb0f93cSkardel span = i; 524abb0f93cSkardel } 525abb0f93cSkardel } 526abb0f93cSkardel 527abb0f93cSkardel cutoff = (cutoff + span) / 2; 528abb0f93cSkardel 529abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff)); 530abb0f93cSkardel 531abb0f93cSkardel s = (unsigned char *)buffer; 532abb0f93cSkardel while (s < e) 533abb0f93cSkardel { 534abb0f93cSkardel if (*s == (unsigned char)~0) 535abb0f93cSkardel { 536abb0f93cSkardel *s = '?'; 537abb0f93cSkardel } 538abb0f93cSkardel else 539abb0f93cSkardel { 540abb0f93cSkardel *s = (*s >= cutoff) ? *b : *c; 541abb0f93cSkardel } 542abb0f93cSkardel s++; 543abb0f93cSkardel if (*b) b++; 544abb0f93cSkardel if (*c) c++; 545abb0f93cSkardel } 546abb0f93cSkardel 5475d681e99Schristos *s = '\0'; 5485d681e99Schristos 549abb0f93cSkardel if (rtc == CVT_NONE) 550abb0f93cSkardel { 551abb0f93cSkardel rtc = convert_rawdcf(buffer, size, &dcfparameter, clock_time); 552abb0f93cSkardel if (rtc == CVT_OK) 553abb0f93cSkardel { 554abb0f93cSkardel time_t newtime; 555abb0f93cSkardel 556abb0f93cSkardel newtime = parse_to_unixtime(clock_time, &rtc); 557abb0f93cSkardel if ((rtc == CVT_OK) && t) 558abb0f93cSkardel { 5595d681e99Schristos if ((newtime - t->tcode) <= 600) /* require a successful telegram within last 10 minutes */ 560abb0f93cSkardel { 5615d681e99Schristos parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: recent timestamp check OK\n")); 562abb0f93cSkardel clock_time->utctime = newtime; 563abb0f93cSkardel } 564abb0f93cSkardel else 565abb0f93cSkardel { 5665d681e99Schristos parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: recent timestamp check FAIL - ignore timestamp\n")); 5675d681e99Schristos rtc = CVT_SKIP; 568abb0f93cSkardel } 569abb0f93cSkardel t->tcode = newtime; 570abb0f93cSkardel } 571abb0f93cSkardel } 572abb0f93cSkardel } 573abb0f93cSkardel 574abb0f93cSkardel return rtc; 575abb0f93cSkardel } 576abb0f93cSkardel 577abb0f93cSkardel /* 5787476e6e4Schristos * parse_pps_fnc_t pps_rawdcf 579abb0f93cSkardel * 580abb0f93cSkardel * currently a very stupid version - should be extended to decode 581abb0f93cSkardel * also ones and zeros (which is easy) 582abb0f93cSkardel */ 583abb0f93cSkardel /*ARGSUSED*/ 584abb0f93cSkardel static u_long 585abb0f93cSkardel pps_rawdcf( 586abb0f93cSkardel parse_t *parseio, 587abb0f93cSkardel int status, 588abb0f93cSkardel timestamp_t *ptime 589abb0f93cSkardel ) 590abb0f93cSkardel { 591abb0f93cSkardel if (!status) /* negative edge for simpler wiring (Rx->DCD) */ 592abb0f93cSkardel { 593abb0f93cSkardel parseio->parse_dtime.parse_ptime = *ptime; 594abb0f93cSkardel parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS; 595abb0f93cSkardel } 596abb0f93cSkardel 597abb0f93cSkardel return CVT_NONE; 598abb0f93cSkardel } 599abb0f93cSkardel 6005d681e99Schristos static long 6015d681e99Schristos calc_usecdiff( 6025d681e99Schristos timestamp_t *ref, 6035d681e99Schristos timestamp_t *base, 6045d681e99Schristos long offset 6055d681e99Schristos ) 6065d681e99Schristos { 6075d681e99Schristos struct timeval delta; 6085d681e99Schristos long delta_usec = 0; 6095d681e99Schristos 6105d681e99Schristos #ifdef PARSEKERNEL 6115d681e99Schristos delta.tv_sec = ref->tv.tv_sec - offset - base->tv.tv_sec; 6125d681e99Schristos delta.tv_usec = ref->tv.tv_usec - base->tv.tv_usec; 6135d681e99Schristos if (delta.tv_usec < 0) 6145d681e99Schristos { 6155d681e99Schristos delta.tv_sec -= 1; 6165d681e99Schristos delta.tv_usec += 1000000; 6175d681e99Schristos } 6185d681e99Schristos #else 6195d681e99Schristos l_fp delt; 6205d681e99Schristos 6215d681e99Schristos delt = ref->fp; 6225d681e99Schristos delt.l_i -= offset; 6235d681e99Schristos L_SUB(&delt, &base->fp); 6245d681e99Schristos TSTOTV(&delt, &delta); 6255d681e99Schristos #endif 6265d681e99Schristos 6275d681e99Schristos delta_usec = 1000000 * (int32_t)delta.tv_sec + delta.tv_usec; 6285d681e99Schristos return delta_usec; 6295d681e99Schristos } 6305d681e99Schristos 631abb0f93cSkardel static u_long 632abb0f93cSkardel snt_rawdcf( 633abb0f93cSkardel parse_t *parseio, 634abb0f93cSkardel timestamp_t *ptime 635abb0f93cSkardel ) 636abb0f93cSkardel { 6375d681e99Schristos /* 6385d681e99Schristos * only synthesize if all of following conditions are met: 6395d681e99Schristos * - CVT_OK parse_status (we have a time stamp base) 6405d681e99Schristos * - ABS(ptime - tminute - (parse_index - 1) sec) < 500ms (spaced by 1 sec +- 500ms) 6415d681e99Schristos * - minute marker is available (confirms minute raster as base) 6425d681e99Schristos */ 6435d681e99Schristos last_tcode_t *t = (last_tcode_t *)parseio->parse_pdata; 6445d681e99Schristos long delta_usec = -1; 6455d681e99Schristos 6465d681e99Schristos if (t != NULL && t->tminute.tv.tv_sec != 0) { 6475d681e99Schristos delta_usec = calc_usecdiff(ptime, &t->tminute, parseio->parse_index - 1); 6485d681e99Schristos if (delta_usec < 0) 6495d681e99Schristos delta_usec = -delta_usec; 6505d681e99Schristos } 6515d681e99Schristos 6525d681e99Schristos parseprintf(DD_RAWDCF,("parse: snt_rawdcf: synth for offset %d seconds - absolute usec error %ld\n", 6535d681e99Schristos parseio->parse_index - 1, delta_usec)); 6545d681e99Schristos 6555d681e99Schristos if (((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) && 6565d681e99Schristos (delta_usec < 500000 && delta_usec >= 0)) /* only if minute marker is available */ 657abb0f93cSkardel { 658abb0f93cSkardel parseio->parse_dtime.parse_stime = *ptime; 659abb0f93cSkardel 660abb0f93cSkardel #ifdef PARSEKERNEL 661abb0f93cSkardel parseio->parse_dtime.parse_time.tv.tv_sec++; 662abb0f93cSkardel #else 663abb0f93cSkardel parseio->parse_dtime.parse_time.fp.l_ui++; 664abb0f93cSkardel #endif 665abb0f93cSkardel 666abb0f93cSkardel parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1)); 667abb0f93cSkardel 668abb0f93cSkardel return updatetimeinfo(parseio, parseio->parse_lstate); 669abb0f93cSkardel } 670abb0f93cSkardel return CVT_NONE; 671abb0f93cSkardel } 672abb0f93cSkardel 673abb0f93cSkardel /* 6747476e6e4Schristos * parse_inp_fnc_t inp_rawdcf 675abb0f93cSkardel * 676abb0f93cSkardel * grab DCF77 data from input stream 677abb0f93cSkardel */ 678abb0f93cSkardel static u_long 679abb0f93cSkardel inp_rawdcf( 680abb0f93cSkardel parse_t *parseio, 6817476e6e4Schristos char ch, 682abb0f93cSkardel timestamp_t *tstamp 683abb0f93cSkardel ) 684abb0f93cSkardel { 685abb0f93cSkardel static struct timeval timeout = { 1, 500000 }; /* 1.5 secongs denote second #60 */ 686abb0f93cSkardel 6878b8da087Schristos parseprintf(DD_PARSE, ("inp_rawdcf(0x%p, 0x%x, ...)\n", (void*)parseio, ch)); 688abb0f93cSkardel 689abb0f93cSkardel parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */ 690abb0f93cSkardel 691abb0f93cSkardel if (parse_timedout(parseio, tstamp, &timeout)) 692abb0f93cSkardel { 6935d681e99Schristos last_tcode_t *t = (last_tcode_t *)parseio->parse_pdata; 6945d681e99Schristos long delta_usec; 695abb0f93cSkardel 6965d681e99Schristos parseprintf(DD_RAWDCF, ("inp_rawdcf: time out seen\n")); 6975d681e99Schristos /* finish collection */ 698abb0f93cSkardel (void) parse_end(parseio); 6995d681e99Schristos 7005d681e99Schristos if (t != NULL) 7015d681e99Schristos { 7025d681e99Schristos /* remember minute start sample time if timeouts occur in minute raster */ 7035d681e99Schristos if (t->timeout.tv.tv_sec != 0) 7045d681e99Schristos { 7055d681e99Schristos delta_usec = calc_usecdiff(tstamp, &t->timeout, 60); 7065d681e99Schristos if (delta_usec < 0) 7075d681e99Schristos delta_usec = -delta_usec; 7085d681e99Schristos } 7095d681e99Schristos else 7105d681e99Schristos { 7115d681e99Schristos delta_usec = -1; 7125d681e99Schristos } 7135d681e99Schristos 7145d681e99Schristos if (delta_usec < 500000 && delta_usec >= 0) 7155d681e99Schristos { 7165d681e99Schristos parseprintf(DD_RAWDCF, ("inp_rawdcf: timeout time difference %ld usec - minute marker set\n", delta_usec)); 7175d681e99Schristos /* collect minute markers only if spaced by 60 seconds */ 7185d681e99Schristos t->tminute = *tstamp; 7195d681e99Schristos } 7205d681e99Schristos else 7215d681e99Schristos { 7225d681e99Schristos parseprintf(DD_RAWDCF, ("inp_rawdcf: timeout time difference %ld usec - minute marker cleared\n", delta_usec)); 7235d681e99Schristos memset((char *)&t->tminute, 0, sizeof(t->tminute)); 7245d681e99Schristos } 7255d681e99Schristos t->timeout = *tstamp; 7265d681e99Schristos } 727abb0f93cSkardel (void) parse_addchar(parseio, ch); 7285d681e99Schristos 7295d681e99Schristos /* pass up to higher layers */ 730abb0f93cSkardel return PARSE_INP_TIME; 731abb0f93cSkardel } 732abb0f93cSkardel else 733abb0f93cSkardel { 734abb0f93cSkardel unsigned int rtc; 735abb0f93cSkardel 736abb0f93cSkardel rtc = parse_addchar(parseio, ch); 737abb0f93cSkardel if (rtc == PARSE_INP_SKIP) 738abb0f93cSkardel { 739abb0f93cSkardel if (snt_rawdcf(parseio, tstamp) == CVT_OK) 740abb0f93cSkardel return PARSE_INP_SYNTH; 741abb0f93cSkardel } 742abb0f93cSkardel return rtc; 743abb0f93cSkardel } 744abb0f93cSkardel } 745abb0f93cSkardel 746abb0f93cSkardel #else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */ 747*eabc0478Schristos NONEMPTY_TRANSLATION_UNIT 748abb0f93cSkardel #endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_RAWDCF) */ 749abb0f93cSkardel 750abb0f93cSkardel /* 751abb0f93cSkardel * History: 752abb0f93cSkardel * 753abb0f93cSkardel * clk_rawdcf.c,v 754abb0f93cSkardel * Revision 4.18 2006/06/22 18:40:01 kardel 755abb0f93cSkardel * clean up signedness (gcc 4) 756abb0f93cSkardel * 757abb0f93cSkardel * Revision 4.17 2006/01/22 16:01:55 kardel 758abb0f93cSkardel * update version information 759abb0f93cSkardel * 760abb0f93cSkardel * Revision 4.16 2006/01/22 15:51:22 kardel 761abb0f93cSkardel * generate reasonable timecode output on invalid input 762abb0f93cSkardel * 763abb0f93cSkardel * Revision 4.15 2005/08/06 19:17:06 kardel 764abb0f93cSkardel * clean log output 765abb0f93cSkardel * 766abb0f93cSkardel * Revision 4.14 2005/08/06 17:39:40 kardel 767abb0f93cSkardel * cleanup size handling wrt/ to buffer boundaries 768abb0f93cSkardel * 769abb0f93cSkardel * Revision 4.13 2005/04/16 17:32:10 kardel 770abb0f93cSkardel * update copyright 771abb0f93cSkardel * 772abb0f93cSkardel * Revision 4.12 2004/11/14 15:29:41 kardel 773abb0f93cSkardel * support PPSAPI, upgrade Copyright to Berkeley style 774abb0f93cSkardel * 775abb0f93cSkardel * Revision 4.9 1999/12/06 13:42:23 kardel 776abb0f93cSkardel * transfer correctly converted time codes always into tcode 777abb0f93cSkardel * 778abb0f93cSkardel * Revision 4.8 1999/11/28 09:13:50 kardel 779abb0f93cSkardel * RECON_4_0_98F 780abb0f93cSkardel * 781abb0f93cSkardel * Revision 4.7 1999/04/01 20:07:20 kardel 782abb0f93cSkardel * added checking for minutie increment of timestamps in clk_rawdcf.c 783abb0f93cSkardel * 784abb0f93cSkardel * Revision 4.6 1998/06/14 21:09:37 kardel 785abb0f93cSkardel * Sun acc cleanup 786abb0f93cSkardel * 787abb0f93cSkardel * Revision 4.5 1998/06/13 12:04:16 kardel 788abb0f93cSkardel * fix SYSV clock name clash 789abb0f93cSkardel * 790abb0f93cSkardel * Revision 4.4 1998/06/12 15:22:28 kardel 791abb0f93cSkardel * fix prototypes 792abb0f93cSkardel * 793abb0f93cSkardel * Revision 4.3 1998/06/06 18:33:36 kardel 794abb0f93cSkardel * simplified condidional compile expression 795abb0f93cSkardel * 796abb0f93cSkardel * Revision 4.2 1998/05/24 11:04:18 kardel 797abb0f93cSkardel * triggering PPS on negative edge for simpler wiring (Rx->DCD) 798abb0f93cSkardel * 799abb0f93cSkardel * Revision 4.1 1998/05/24 09:39:53 kardel 800abb0f93cSkardel * implementation of the new IO handling model 801abb0f93cSkardel * 802abb0f93cSkardel * Revision 4.0 1998/04/10 19:45:30 kardel 803abb0f93cSkardel * Start 4.0 release version numbering 804abb0f93cSkardel * 805abb0f93cSkardel * from V3 3.24 log info deleted 1998/04/11 kardel 806abb0f93cSkardel * 807abb0f93cSkardel */ 808