/*      $NetBSD: clk_hopf6021.c,v 1.8 2024/08/18 20:47:17 christos Exp $        */

/*
* /src/NTP/ntp4-dev/libparse/clk_hopf6021.c,v 4.10 2004/11/14 15:29:41 kardel RELEASE_20050508_A
*
* clk_hopf6021.c,v 4.10 2004/11/14 15:29:41 kardel RELEASE_20050508_A
*
* Radiocode Clocks HOPF Funkuhr 6021 mit serieller Schnittstelle
* base code version from 24th Nov 1995 - history at end
*
* Created by F.Schnekenbuehl <[email protected]> from clk_rcc8000.c
* Nortel DASA Network Systems GmbH, Department: ND250
* A Joint venture of Daimler-Benz Aerospace and Nortel
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/

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

#include "ntp_types.h"

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

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

#include "parse.h"

#ifndef PARSESTREAM
#include "ntp_stdlib.h"
#include <stdio.h>
#else
#include "sys/parsestreams.h"
extern int printf (const char *, ...);
#endif

#include "ascii.h"

/*
* hopf Funkuhr 6021
*      used with 9600,8N1,
*      UTC ueber serielle Schnittstelle
*      Sekundenvorlauf ON
*      ETX zum Sekundenvorlauf ON
*      Datenstring 6021
*      Ausgabe Uhrzeit und Datum
*      Senden mit Steuerzeichen
*      Senden sekuendlich
*/

/*
*  Type 6021 Serial Output format
*
*      000000000011111111 / char
*      012345678901234567 \ position
*      sABHHMMSSDDMMYYnre  Actual
*       C4110046231195     Parse
*      s              enr  Check
*
*  s = STX (0x02), e = ETX (0x03)
*  n = NL  (0x0A), r = CR  (0x0D)
*
*  A B - Status and weekday
*
*  A - Status
*
*      8 4 2 1
*      x x x 0  - no announcement
*      x x x 1  - Summertime - wintertime - summertime announcement
*      x x 0 x  - Wintertime
*      x x 1 x  - Summertime
*      0 0 x x  - Time/Date invalid
*      0 1 x x  - Internal clock used
*      1 0 x x  - Radio clock
*      1 1 x x  - Radio clock highprecision
*
*  B - 8 4 2 1
*      0 x x x  - MESZ/MEZ
*      1 x x x  - UTC
*      x 0 0 1  - Monday
*      x 0 1 0  - Tuesday
*      x 0 1 1  - Wednesday
*      x 1 0 0  - Thursday
*      x 1 0 1  - Friday
*      x 1 1 0  - Saturday
*      x 1 1 1  - Sunday
*/

#define HOPF_DSTWARN    0x01    /* DST switch warning */
#define HOPF_DST        0x02    /* DST in effect */

#define HOPF_MODE       0x0C    /* operation mode mask */
#define  HOPF_INVALID   0x00    /* no time code available */
#define  HOPF_INTERNAL  0x04    /* internal clock */
#define  HOPF_RADIO     0x08    /* radio clock */
#define  HOPF_RADIOHP   0x0C    /* high precision radio clock */

#define HOPF_UTC        0x08    /* time code in UTC */
#define HOPF_WMASK      0x07    /* mask for weekday code */

static struct format hopf6021_fmt =
{
       {
               {  9, 2 }, {11, 2}, { 13, 2}, /* Day, Month, Year */
               {  3, 2 }, { 5, 2}, {  7, 2}, /* Hour, Minute, Second */
               {  2, 1 }, { 1, 1}, {  0, 0}, /* Weekday, Flags, Zone */
               /* ... */
       },
       (const unsigned char *)"\002              \n\r\003",
       0
};

#define OFFS(x) format->field_offsets[(x)].offset
#define STOI(x, y) Stoi(&buffer[OFFS(x)], y, format->field_offsets[(x)].length)

static parse_cvt_fnc_t cvt_hopf6021;
static parse_inp_fnc_t inp_hopf6021;
static unsigned char   hexval(unsigned char);

clockformat_t clock_hopf6021 =
{
 inp_hopf6021,                 /* HOPF 6021 input handling */
 cvt_hopf6021,                 /* Radiocode clock conversion */
 0,                            /* no direct PPS monitoring */
 (void *)&hopf6021_fmt,        /* conversion configuration */
 "hopf Funkuhr 6021",          /* clock format name */
 19,                           /* string buffer */
 0                            /* private data length, no private data */
};

/* parse_cvt_fnc_t cvt_hopf6021 */
static u_long
cvt_hopf6021(
            unsigned char *buffer,
            int            size,
            struct format *format,
            clocktime_t   *clock_time,
            void          *local
            )
{
       unsigned char status,weekday;

       if (!Strok(buffer, format->fixed_string))
       {
               return CVT_NONE;
       }

       if (  STOI(O_DAY,   &clock_time->day)    ||
             STOI(O_MONTH, &clock_time->month)  ||
             STOI(O_YEAR,  &clock_time->year)   ||
             STOI(O_HOUR,  &clock_time->hour)   ||
             STOI(O_MIN,   &clock_time->minute) ||
             STOI(O_SEC,   &clock_time->second)
             )
       {
               return CVT_FAIL|CVT_BADFMT;
       }

       clock_time->usecond = 0;
       clock_time->flags   = 0;

       status  = hexval(buffer[OFFS(O_FLAGS)]);
       weekday = hexval(buffer[OFFS(O_WDAY)]);

       if ((status == 0xFF) || (weekday == 0xFF))
       {
               return CVT_FAIL|CVT_BADFMT;
       }

       if (weekday & HOPF_UTC)
       {
               clock_time->flags     |= PARSEB_UTC;
               clock_time->utcoffset  = 0;
       }
       else if (status & HOPF_DST)
       {
               clock_time->flags     |= PARSEB_DST;
               clock_time->utcoffset  = -2*60*60; /* MET DST */
       }
       else
       {
               clock_time->utcoffset  = -1*60*60; /* MET */
       }

       if (status & HOPF_DSTWARN)
       {
               clock_time->flags |= PARSEB_ANNOUNCE;
       }

       switch (status & HOPF_MODE)
       {
           default:    /* dummy: we cover all 4 cases. */
           case HOPF_INVALID:  /* Time/Date invalid */
               clock_time->flags |= PARSEB_POWERUP;
               break;

           case HOPF_INTERNAL: /* internal clock */
               clock_time->flags |= PARSEB_NOSYNC;
               break;

           case HOPF_RADIO:    /* Radio clock */
           case HOPF_RADIOHP:  /* Radio clock high precision */
               break;
       }

       return CVT_OK;
}

/*
* parse_inp_fnc_t inp_hopf6021
*
* grab data from input stream
*/
static u_long
inp_hopf6021(
            parse_t      *parseio,
            char         ch,
            timestamp_t  *tstamp
         )
{
       unsigned int rtc;

       parseprintf(DD_PARSE, ("inp_hopf6021(0x%p, 0x%x, ...)\n", (void*)parseio, ch));

       switch (ch)
       {
       case ETX:
               parseprintf(DD_PARSE, ("inp_hopf6021: EOL seen\n"));
               parseio->parse_dtime.parse_stime = *tstamp; /* collect timestamp */
               if ((rtc = parse_addchar(parseio, ch)) == PARSE_INP_SKIP)
                       return parse_end(parseio);
               else
                       return rtc;

       default:
               return parse_addchar(parseio, ch);
       }
}

/*
* convert a hex-digit to numeric value
*/
static unsigned char
hexval(
       unsigned char ch
       )
{
       unsigned int dv;

       if ((dv = ch - '0') >= 10u)
       {
               if ((dv -= 'A'-'0') < 6u || (dv -= 'a'-'A') < 6u)
               {
                       dv += 10;
               }
               else
               {
                       dv = 0xFF;
               }
       }
       return (unsigned char)dv;
}

#else /* not (REFCLOCK && CLOCK_PARSE && CLOCK_HOPF6021) */
NONEMPTY_TRANSLATION_UNIT
#endif /* not (REFCLOCK && CLOCK_PARSE && CLOCK_HOPF6021) */

/*
* History:
*
* clk_hopf6021.c,v
* Revision 4.10  2004/11/14 15:29:41  kardel
* support PPSAPI, upgrade Copyright to Berkeley style
*
* Revision 4.7  1999/11/28 09:13:49  kardel
* RECON_4_0_98F
*
* Revision 4.6  1998/11/15 20:27:57  kardel
* Release 4.0.73e13 reconcilation
*
* Revision 4.5  1998/06/14 21:09:35  kardel
* Sun acc cleanup
*
* Revision 4.4  1998/06/13 12:02:38  kardel
* fix SYSV clock name clash
*
* Revision 4.3  1998/06/12 15:22:27  kardel
* fix prototypes
*
* Revision 4.2  1998/06/12 09:13:25  kardel
* conditional compile macros fixed
* printf prototype
*
* Revision 4.1  1998/05/24 09:39:52  kardel
* implementation of the new IO handling model
*
* Revision 4.0  1998/04/10 19:45:29  kardel
* Start 4.0 release version numbering
*
* from V3 3.6 log info deleted 1998/04/11 kardel
*/