From: CPrice Date: Sat, 22 Feb 2003 10:47:19 +0000 (-0600) Subject: Arcron clock - enchanged driver and documentation, additional X-Git-Tag: NTP_4_1_1C_RC2~13 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=2fdcbf352f2743dc8f25880161189231ba02df0d;p=thirdparty%2Fntp.git Arcron clock - enchanged driver and documentation, additional features included bk: 3e575537mllNZPiT0skLLqcXf8Na8w --- diff --git a/html/driver27.htm b/html/driver27.htm index 686e985b1b..93581ddec6 100644 --- a/html/driver27.htm +++ b/html/driver27.htm @@ -2,37 +2,49 @@ - Arcron MSF Receiver + <TITLE>Arcron MSF/DCF/WWVB Receiver

-Arcron MSF Receiver

+Arcron MSF/DCF/WWVB Receiver

Synopsis

Address: 127.127.27.u -
Reference ID: MSFa +
Reference ID: MSFa / MSF / DCF / WWVB
Driver ID: MSF_ARCRON
Serial Port: /dev/arcu; 300 baud, 8-bits, 2-stop, no parity
Features: tty_clk

Description

-This driver supports the Arcron MSF receiver, and would probably also support -the DCF77 variant of the same clock. The clock reports its ID as ``MSFa'' -to indicate MSF as a source and the use of the ARCRON driver. - -

This documentation describes version V1.1 (1997/06/23) of the source -and has been tested (amongst others) against ntpd3-5.90 on Solaris-1 (SunOS +This driver supports the Arcron MSF, DCF and WWVB receivers. The clock +reports its ID as ``MSFa'', ``MSF'', ``DCF'' +or ``WWVB'' to indicate the time source. + +

This documentation describes version V1.3 (2003/2/21) of the source +and has been tested against ntp 4.1.0 on linux x86. Changes from v1.1 include +patches to work with the new ntp-4 code, clock support for DCF and WWVB +configurable via mode flag, an option to ingore resync requests (for those of +us at the fringes of the WWVB signal, for instance), averaging of the signal +quality poll and several bug fixes, code cleanup and standardizations. In +all other respects, the driver works as per v1.1 if a mode is not +specified. + +

To use the alternate modes, the mode flag must be specified. If the mode +flag is 0, or unspecified, the orginal MSF version is assumed. This should +assure backwards compatibility and should not break existing setups. + +

The previous documenation described version V1.1 (1997/06/23) of the source +and had been tested (amoungst others) against ntpd3-5.90 on Solaris-1 (SunOS 4.1.3_U1 on an SS1 serving as a router and firewall) and against ntpd3-5.90 -on Solaris-2.5 (on a SS1+ and TurboSPARC 170MHz). This code will probably -work, and show increased stability, reduced jitter and more efficiency -(fewer context switches) with the tty_clk discipline/STREAMS module -installed, but this has not been tested. For a to-do list see the comments -at the start of the code. +on Solaris-2.5 (on a SS1+ and TurboSPARC 170MHz). This code claimed to +increase stability, reduce jitter and more efficiency (fewer context switches) +with the tty_clk discipline/STREAMS module installed, but this has +not been tested. For a to-do list see the comments at the start of the code.

This code has been significantly slimmed down since the V1.0 version, roughly halving the memory footprint of its code and data. @@ -46,25 +58,31 @@ with thanks. The code was originally made to work with the clock by Damon Hart-Davis. Thanks also to Lyndon David for some of the specifications of the clock. +Christopher Price added enhanced +support for the MSF, DCF and WWVB clocks.

There is support for a Tcl/Tk monitor written by Derek Mulcahy that -examines the output stats; see the ARC -Rugby MSF Receiver page for more details and the code. +examines the output stats; see the ARC +Rugby MSF Receiver page for more details and the code. Information on the +WWVB version is available from Atomic Time +as their Atomic Time PC.

Look at the notes at the start of the code for further information; some of the more important details follow.

The driver interrogates the clock at each poll (ie every 64s by default) for a timestamp. The clock responds at the start of the next second (with -the start bit of the first byte being on-time). The time is in `local' -format, including the daylight savings adjustment when it is in effect. -The driver code converts the time back to UTC. +the start bit of the first byte being on-time). In the default or orignal MSF +mode, the time is in `local' format, including the daylight savings adjustment +when it is in effect. The driver code converts the time back to UTC. In modes +1-3 the driver can be configured for UTC or local time depending on the setting +of flag1. -

The clock claims to be accurate to within about 20ms of the MSF-broadcast +

The clock claims to be accurate to within about 20ms of the broadcast time, and given the low data transmission speed from clock to host, and -the fact that the clock is not in continuous sync with MSF, it seems sensible -to set the `precision' of this clock to -5 or -4, -4 being used in this -code, which builds in a reported dispersion of over 63ms (ie says ``This +the fact that the clock is not in continuous sync with the signal, it seems +sensible to set the `precision' of this clock to -5 or -4, -4 being used in +this code, which builds in a reported dispersion of over 63ms (ie says ``This clock is not very good.''). You can improve the reported precision to -4 (and thus reduce the base dispersion to about 31ms) by setting the fudge flag3 to 1. @@ -78,32 +96,33 @@ such as GPS.

By default this clock reports itself to be at stratum 2 rather than the usual stratum 0 for a refclock, because it is not really suited to be used as other than a backup source. The stratum reported can be changed -with the fudge directive to be whatever you like. After careful +with the stratum directive to be whatever you like. After careful monitoring of your clock, and appropriate choice of the time1 fudge factor to remove systematic errors in the clock's reported time, you might fudge the clock to stratum 1 to allow a stratum-2 secondary server to sync to it. -

The driver code arranges to resync the clock to MSF at intervals of -a little less than an hour (deliberately avoiding the same time each hour -to avoid any systematic problems with the signal or host). Whilst resyncing, -the driver supplements the normal polls for time from the clock with polls -for the reception signal quality reported by the clock. If the signal quality -is too low (0--2 out of a range of 0--5), we chose not to trust the clock -until the next resync (which we bring forward by about half an hour). If -we don't catch the resync, and so don't know the signal quality, we do +

In default mode, the driver code arranges to resync the clock to MSF at +intervals of a little less than an hour (deliberately avoiding the same time +each hour to avoid any systematic problems with the signal or host). Whilst +resyncing, the driver supplements the normal polls for time from the clock +with polls for the reception signal quality reported by the clock. If the +signal quality is too low (0--2 out of a range of 0--5), we chose not to trust +the clock until the next resync (which we bring forward by about half an hour). +If we don't catch the resync, and so don't know the signal quality, we do trust the clock (because this would generally be when the signal is very good and a resync happens quickly), but we still bring the next resync -forward and reduce the reported precision (and thus increase reported dispersion). +forward and reduce the reported precision (and thus increase reported +dispersion). -

If we force resyncs to MSF too often we will needlessly exhaust the +

If we force resyncs to the signal too often we will needlessly exhaust the batteries the unit runs from. During clock resync this driver tries to take enough time samples to avoid ntpd losing sync in case this -clock is the current peer. By default the clock would only resync to MSF +clock is the current peer. By default the clock would only resync to the signal about once per day, which would almost certainly not be acceptable for NTP purposes. -

The driver does not force an immediate resync of the clock to MSF when +

The driver does not force an immediate resync of the clock to the signal when it starts up to avoid excessive battery drain in case ntpd is going to be repeatedly restarted for any reason, and also to allow enough samples of the clock to be taken for ntpd to sync immediately @@ -114,7 +133,12 @@ will not accept the timestamps from the clock if the status flag delivered with the time code indicates that the last resync attempt was unsuccessful, so the initial timestamps will be close to reality, even if with up to a day's clock drift in the worst case (the clock by default resyncs to -MSF once per day). +the signal once per day). + +

When alternate modes 1-3 are selected, the driver can be configured to +ignore the resync requests by setting flag2 to 1. This +allows clocks at the fringe of the signal to resync at night when signals +are stronger.

The clock has a peculiar RS232 arrangement where the transmit lines are powered from the receive lines, presumably to minimise battery drain. @@ -126,7 +150,7 @@ Your RS232 interface must drive both +ve and -ve

  • You must (in theory) wait for an echo and a further 10ms between characters
  • -This driver, running on standard Sun hardware, seems to work fine; note +This driver, running on standard Sun and x86 hardware, seems to work fine; note the use of the send_slow() routine to queue up command characters to be sent once every two seconds. @@ -145,7 +169,7 @@ The commands and their responses are:
    Request for signal quality. Answer only valid during (late part of) resync -to MSF signal. The response consists of two characters as follows:
    +to the signal. The response consists of two characters as follows:
      @@ -245,13 +269,13 @@ not `3'. h CR
      -Request to resync to MSF. Can take up from about 30s to 360s. Drains batteries -so should not be used excessively. After this the clock time and date should -be correct and the phase within 20ms of time as transmitted from Rugby -MSF (remember to allow for propagation time). By default the clock resyncs -once per day shortly after 2am (presumably to catch transitions to/from -daylight saving time quickly). With this driver code we resync at least -once per hour to minimise clock wander.
      +Request to resync to signal. Can take up from about 30s to 360s. Drains +batteries so should not be used excessively. After this the clock time and +date should be correct and the phase within 20ms of time as transmitted from +the source signal (remember to allow for propagation time). By default the +clock resyncs once per day in the late evening/early morning (presumably to +catch transitions to/from daylight saving time quickly). With this driver +code, by default, we resync at least once per hour to minimise clock wander.
      o CR
      @@ -335,25 +359,34 @@ always 1 bit 3
      -always 0
      +(MSF) always 0
      +(WWVB) Leap year indicator bit
      + 0 = non-leap year
      + 1 = leap year
      bit 2
      -= 1 if UTC is in effect (reverse of bit 1)
      += (MSF) 1 if UTC is in effect (reverse of bit 1)
      += (WWVB) Leap second warning bit +
      bit 1
      -= 1 if BST is in effect (reverse of bit 2)
      += (MSF) 1 if BST is in effect (reverse of bit 2)
      += (WWVB) 0 if ST is in effect, 1 if DST is in effect, 1 if transition from ST +with bit 0 set to 0
      bit 0
      -= 1 if BST/UTC change pending
      += (MSF) 1 if BST/UTC change pending
      += (WWVB) 0 if ST is in effect, 1 if DST is in effect, 0 if transition from +DST with bit 1 set to 0
    1. @@ -394,14 +427,17 @@ bit 3 bit 2
      -= 1 if last resync failed (though officially undefined for the MSF clock)
      += 1 if last resync failed (though officially undefined for the MSF clock, +officially defined for WWVB)
      bit 1
      -= 1 if at least one reception attempt since 0230 for the MSF clock was -successful (0300 for the DCF77 clock)
      += 1 if at least one reception attempt was successful
      +(MSF) since 0230
      +(DCF) since 0300
      +(WWVB) resets if not successful between 0300-0400
      bit 0
      @@ -412,9 +448,8 @@ at power-up), and set to 1 after first successful resync attempt.
    The driver only accepts time from the clock if the bottom three bits of -the status byte are 011. The leap-year logic for computing day-in-year -is only valid until 2099, and the clock will ignore stamps from the clock -that claim BST is in effect in the first hour of each year. If the UK parliament +the status byte are 011 or flag2 is set to 1 to +ignore resync requests. For the MSF clock, if the UK parliament decides to move us to +0100/+0200 time as opposed to the current +0000/+0100 time, it is not clear what effect that will have on the time broadcast by MSF, and therefore on this driver's usefulness. @@ -430,6 +465,9 @@ server 127.127.27.0 # ARCRON MSF radio clock(1). # Fudge stratum and other features as required. # ADJUST time1 VALUE FOR YOUR HOST, CLOCK AND LOCATION! fudge 127.127.27.0 stratum 1 time1 0.016 flag3 1 flag4 1 +# WWVB users should change that line to: +server 127.127.27.0 mode 3 # ARCRON WWVB radio clock +fudge 127.127.27.0 stratum 1 time1 0.30 flag1 1 flag3 1 peer 11.22.33.9 # tick(1--2). peer 11.22.33.4 # tock(3), boot/NFS server. @@ -469,26 +507,6 @@ to undefine this, especially if you have better sources of time or your reception is ropey. However, there are many checks built in even with this flag defined. -
    -ARCRON_OWN_FILTER
    - -
    -When defined, the code uses its own median-filter code rather than that -available in ntp_refclock.c since the latter seems to have a minor -bug, at least in version 3-5.90. If this bug goes away this flag should -be turned off to avoid duplication of code. (The bug, if that's what it -is, causes the last raw offset to be used rather than the median offset.)
    - - -

    Without this defined (and without ARCRON_MULTIPLE_SAMPLES below) -a typical set of offsets reported and used to drive the clock-filter algorithm -is (oldest last): -

    filtoffset=  -4.32  -34.82   -0.78    0.89    2.76    4.58   -3.92   -2.17
    -Look at that spike! - -

    With this defined a typical set of offsets is: -

    filtoffset=  -7.06   -7.06   -2.91   -2.91   -2.91   -1.27   -9.54   -6.70
    -with the repeated values being some evidence of outlyers being discarded.
    ARCRON_MULTIPLE_SAMPLES
    @@ -521,28 +539,12 @@ good experience with the clock and you live on the edge. Note that the if clock signal quality is known good. So maybe just leave this alone. B^) -
    -NSAMPLES
    - -
    -Should be at least 3 to help smooth out sampling jitters. Can be more, -but if made too long can make ntpd overshoot on clock corrections -and can hold onto bad samples longer than you would like. With this set -to 4 and NKEEP set to 3 this allows the occasional bad sample -(in my experience less than 1 value in 10) to be dropped. (Note that there -seems to be some sort of `beat' effect in the offset with a periodicity -of about 7 samples as of this writing (1997/05/11) still under investigation; -a filter of approximately this length should be able to almost completely -suppress this effect.) Note that if the fudge-factor flag3 is -set to 1, a larger NSAMPLES is used.
    - -

    Monitor Data

    Each timecode is written to the clockstats file with a signal quality value appended (`0'--`5' as reported by the clock, or `6' for unknown). -

    Each resync and result (plus gaining or losing MSF sync) is logged to +

    Each resync and result (plus gaining or losing signal sync) is logged to the system log at level LOG_NOTICE; note that each resync drains the unit's batteries, so the syslog entry seems justified. @@ -560,6 +562,18 @@ May 10 12:41:34 oolong ntpd[615]: ARCRON: sync finished, signal quality 3: OK, w Fudge Factors

    +
    +mode 0 | 1 | 2 | 3
    + +
    +Specified the clock hardware model. This parameter is optional, it defaults +to the original mode of operation. +
    Supported modes of operation: +
    0 - Default, Original MSF +
    1 - Updated MSF +
    2 - New DCF77 +
    3 - New WWVB +
    time1 time
    @@ -578,7 +592,7 @@ Not currently used by this driver. stratum number
    -Specifies the driver stratum, in decimal from 0 to 15, with default 0. +Specifies the driver stratum, in decimal from 0 to 15, with default 2. It is suggested that the clock be fudged to stratum 1 so this it is used a backup time source rather than a primary when more accurate sources are available.
    @@ -588,19 +602,24 @@ available.
    Specifies the driver reference identifier, an ASCII string from one to -four characters, with default MSFa.
    +four characters, with default MSFa. When used in modes 1-3, the +driver will report either MSF, DCF, or WWVB, +respectively
    flag1 0 | 1
    -Not used by this driver.
    +(Modes 1-3) If set to 0 (the default), the clock is set to UTC time. If set +to 1, the clock is set to localtime.
    flag2 0 | 1
    -Not used by this driver.
    +(Modes 1-3) If set to 0 (the default), the clock will be forced to resync +approximately every hour. If set to 1, the clock will resync per normal +operations (approximately midnight).
    flag3 0 | 1
    @@ -613,18 +632,14 @@ clock's received signal quality is known to be good. flag4 0 | 1
    -If set to 1, a longer-than-normal (8-stage rather than 4-stage) median -filter is used, to provide some extra smoothing of clock output and reduction -in jitter, at the cost of extra clock overshoot. Probably not advisable -unless the server using this clock has other sources it can use to help -mitigate the overshoot.
    +Note used by this driver.

    Additional Information

    Reference Clock Drivers -

    ARC Rugby MSF Receiver +

    ARC Rugby MSF Receiver page 


    diff --git a/ntpd/refclock_arc.c b/ntpd/refclock_arc.c index c771f478f6..61352fd3bd 100644 --- a/ntpd/refclock_arc.c +++ b/ntpd/refclock_arc.c @@ -1,5 +1,5 @@ /* - * refclock_arc - clock driver for ARCRON MSF receivers + * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers */ #ifdef HAVE_CONFIG_H @@ -7,9 +7,11 @@ #endif #if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF) -static const char arc_version[] = { "V1.1 1997/06/23" }; +static const char arc_version[] = { "V1.3 2003/02/21" }; -#undef ARCRON_DEBUG /* Define only while in development... */ +/* define PRE_NTP420 for compatibility to previous versions of NTP (at least + to 4.1.0 */ +#define PRE_NTP420 #ifndef ARCRON_NOT_KEEN #define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */ @@ -28,6 +30,7 @@ static const char arc_version[] = { "V1.1 1997/06/23" }; /* Code by Derek Mulcahy, , 1997. Modifications by Damon Hart-Davis, , 1997. +Modifications by Christopher M. Price, , 2003. THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND. USE AT YOUR OWN RISK. @@ -42,6 +45,33 @@ reproduced. ------------------------------------------------------------------------------- +Christopher's notes: + +MAJOR CHANGES SINCE V1.2 +======================== + 1) Applied patch by Andrey Bray + 2001-02-17 comp.protocols.time.ntp + + 2) Added WWVB support via clock mode command, localtime/UTC time configured + via flag1=(0=UTC, 1=localtime) + + 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync) + + 4) Added simplified conversion from localtime to UTC with dst/bst translation + + 5) Added average signal quality poll + + 6) Fixed a badformat error when no code is available due to stripping + \n & \r's + + 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll + routine + + 8) Lots of code cleanup, including standardized DEBUG macros and removal + of unused code + +------------------------------------------------------------------------------- + Author's original note: I enclose my ntp driver for the Galleon Systems Arc MSF receiver. @@ -306,6 +336,7 @@ Also note h command which starts a resync to MSF signal. #include "ntpd.h" #include "ntp_io.h" #include "ntp_refclock.h" +#include "ntp_calendar.h" #include "ntp_stdlib.h" #include @@ -324,7 +355,7 @@ Also note h command which starts a resync to MSF signal. #endif /* - * This driver supports the ARCRON MSF Radio Controlled Clock + * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock */ /* @@ -335,9 +366,16 @@ Also note h command which starts a resync to MSF signal. #define PRECISION (-4) /* Precision (~63 ms). */ #define HIGHPRECISION (-5) /* If things are going well... */ #define REFID "MSFa" /* Reference ID. */ -#define DESCRIPTION "ARCRON MSF Receiver" +#define REFID_MSF "MSF" /* Reference ID. */ +#define REFID_DCF77 "DCF" /* Reference ID. */ +#define REFID_WWVB "WWVB" /* Reference ID. */ +#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver" -#define NSAMPLESLONG 8 /* Stages of long filter. */ +#ifdef PRE_NTP420 +#define MODE ttlmax +#else +#define MODE ttl +#endif #define LENARC 16 /* Format `o' timecode length. */ @@ -405,22 +443,6 @@ Also note h command which starts a resync to MSF signal. #endif }; -/* Chose filter length dependent on fudge flag 4. */ -#define CHOSENSAMPLES(pp) \ -(((pp)->sloppyclockflag & CLK_FLAG4) ? NSAMPLESLONG : NSAMPLES) - /* -Chose how many filter samples to keep. Several factors are in play. - - 1) Discard at least one sample to allow a spike value to be - discarded. - - 2) Discard about 1-in-8 to 1-in-30 samples to handle spikes. - - 3) Keep an odd number of samples to avoid median value being biased - high or low. -*/ -#define NKEEP(pp) ((CHOSENSAMPLES(pp) - 1 - (CHOSENSAMPLES(pp)>>3)) | 1) - #define DEFAULT_RESYNC_TIME (57*60) /* Gap between resync attempts (s). */ #define RETRY_RESYNC_TIME (27*60) /* Gap to emergency resync attempt. */ #ifdef ARCRON_KEEN @@ -454,6 +476,7 @@ struct arcunit { int quality; /* Quality of reception 0--5 for unit. */ /* We may also use the values -1 or 6 internally. */ + u_long quality_stamp; /* Next time to reset quality average. */ u_long next_resync; /* Next resync time (s) compared to current_time. */ int resyncing; /* Resync in progress if true. */ @@ -463,6 +486,7 @@ struct arcunit { u_long saved_flags; /* Saved fudge flags. */ }; + #ifdef ARCRON_LEAPSECOND_KEEN /* The flag `possible_leap' is set non-zero when any MSF unit thinks a leap-second may have happened. @@ -522,18 +546,16 @@ struct refclock refclock_arc = { /* Queue us up for the next tick. */ #define ENQUEUE(up) \ do { \ - if((up)->ev.next != 0) { break; } /* WHOOPS! */ \ - peer->nextdate = current_time + QUEUETICK; \ + peer->nextaction = current_time + QUEUETICK; \ } while(0) -#if 0 -/* Placeholder event handler---does nothing safely---soaks up lose tick. */ +/* Placeholder event handler---does nothing safely---soaks up loose tick. */ static void dummy_event_handler( struct peer *peer ) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: dummy_event_handler() called.\n"); } #endif } @@ -558,7 +580,7 @@ arc_event_handler( register struct arcunit *up = (struct arcunit *)pp->unitptr; int i; char c; -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 2) { printf("arc: arc_event_handler() called.\n"); } #endif @@ -572,12 +594,13 @@ arc_event_handler( if(write(pp->io.fd, &c, 1) != 1) { msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd); } -#ifdef ARCRON_DEBUG +#ifdef DEBUG else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); } #endif } + + ENQUEUE(up); } -#endif /* 0 */ /* * arc_start - open the devices and initialize data for processing @@ -597,7 +620,7 @@ arc_start( #endif msyslog(LOG_NOTICE, "ARCRON: %s: opening unit %d", arc_version, unit); -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: %s: attempt to open unit %d.\n", arc_version, unit); } @@ -612,7 +635,7 @@ arc_start( (void)sprintf(device, DEVICE, unit); if (!(fd = refclock_open(device, SPEED, LDISC_CLK))) return(0); -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: unit %d using open().\n", unit); } #endif fd = open(device, OPEN_FLAGS); @@ -624,9 +647,9 @@ arc_start( } fcntl(fd, F_SETFL, 0); /* clear the descriptor flags */ -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) - { printf("Opened RS232 port with file descriptor %d.\n", fd); } + { printf("arc: opened RS232 port with file descriptor %d.\n", fd); } #endif #ifdef HAVE_TERMIOS @@ -667,7 +690,27 @@ arc_start( peer->precision = PRECISION; peer->stratum = 2; /* Default to stratum 2 not 0. */ pp->clockdesc = DESCRIPTION; - memcpy((char *)&pp->refid, REFID, 4); + if (peer->MODE > 3) { + msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE); + return 0; + } +#ifdef DEBUG + if(debug) { printf("arc: mode = %d.\n", peer->MODE); } +#endif + switch (peer->MODE) { + case 1: + memcpy((char *)&pp->refid, REFID_MSF, 4); + break; + case 2: + memcpy((char *)&pp->refid, REFID_DCF77, 4); + break; + case 3: + memcpy((char *)&pp->refid, REFID_WWVB, 4); + break; + default: + memcpy((char *)&pp->refid, REFID, 4); + break; + } /* Spread out resyncs so that they should remain separated. */ up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009; @@ -686,6 +729,11 @@ arc_start( #else up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */ #endif + + peer->action = arc_event_handler; + + ENQUEUE(up); + return(1); } @@ -702,6 +750,8 @@ arc_shutdown( register struct arcunit *up; struct refclockproc *pp; + peer->action = dummy_event_handler; + pp = peer->procptr; up = (struct arcunit *)pp->unitptr; io_closeclock(&pp->io); @@ -740,11 +790,11 @@ send_slow( int sl = strlen(s); int spaceleft = space_left(up); -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); } #endif if(spaceleft < sl) { /* Should not normally happen... */ -#ifdef ARCRON_DEBUG +#ifdef DEBUG msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)", sl, spaceleft); #endif @@ -776,8 +826,11 @@ arc_receive( struct refclockproc *pp; struct peer *peer; char c; - int i, n, wday, month, bst, status; + int i, n, wday, month, flags, status; int arc_last_offset; + static int quality_average = 0; + static int quality_sum = 0; + static int quality_polls = 0; /* * Initialize pointers and read the timecode and timestamp @@ -857,7 +910,7 @@ arc_receive( handle for tty_clk or somesuch kernel timestamper. */ if(arc_last_offset > LENARC) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: input code too long (%d cf %d); rejected.\n", arc_last_offset, LENARC); @@ -869,7 +922,7 @@ arc_receive( } L_SUBUF(×tamp, charoffsets[arc_last_offset]); -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 1) { printf( "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n", @@ -898,7 +951,7 @@ arc_receive( L_ISGEQ(&(up->lastrec), ×tamp)) #endif { -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 1) { printf("arc: system timestamp captured.\n"); #ifdef ARCRON_MULTIPLE_SAMPLES @@ -922,7 +975,7 @@ arc_receive( /* eg on receipt of the \r coming in on its own after the */ /* timecode. */ if(pp->lencode >= LENARC) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug && (rbufp->recv_buffer[0] != '\r')) { printf("arc: rubbish in pp->a_lastcode[].\n"); } #endif @@ -947,11 +1000,12 @@ arc_receive( */ if((c == 'o') && (pp->lencode == 1)) { L_CLR(&(up->lastrec)); -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 1) { printf("arc: clearing timestamp.\n"); } #endif } } + if (pp->lencode == 0) return; /* Handle a quality message. */ if(pp->a_lastcode[0] == 'g') { @@ -963,17 +1017,31 @@ arc_receive( if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) || ((r & 0x70) != 0x30)) { /* Badly formatted response. */ -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); } #endif return; } if(r == '3') { /* Only use quality value whilst sync in progress. */ - up->quality = (q & 0xf); + if (up->quality_stamp < current_time) { + struct calendar cal; + ulong new_stamp; + + get_systime (&new_stamp); + caljulian (new_stamp, &cal); + up->quality_stamp = + current_time + 60 - cal.second + 5; + quality_sum = 0; + quality_polls = 0; + } + quality_sum += (q & 0xf); + quality_polls++; + quality_average = (quality_sum / quality_polls); #ifdef DEBUG - if(debug) { printf("arc: signal quality %d.\n", up->quality); } + if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); } #endif } else if( /* (r == '2') && */ up->resyncing) { + up->quality = quality_average; #ifdef DEBUG if(debug) { @@ -987,6 +1055,9 @@ arc_receive( up->quality, quality_action(up->quality)); up->resyncing = 0; /* Resync is over. */ + quality_average = 0; + quality_sum = 0; + quality_polls = 0; #ifdef ARCRON_KEEN /* Clock quality dubious; resync earlier than usual. */ @@ -1011,13 +1082,13 @@ arc_receive( /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */ -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); } #endif /* But check that we actually captured a system timestamp on it. */ if(L_ISZERO(&(up->lastrec))) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); } #endif pp->lencode = 0; @@ -1033,22 +1104,26 @@ arc_receive( pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ? '6' : ('0' + up->quality)); pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */ - record_clock_stats(&peer->srcadr, pp->a_lastcode); +#ifdef PRE_NTP420 /* We don't use the micro-/milli- second part... */ pp->usec = 0; pp->msec = 0; - +#else + /* We don't use the nano-second part... */ + pp->nsec = 0; +#endif n = sscanf(pp->a_lastcode, "o%2d%2d%2d%1d%2d%2d%2d%1d%1d", &pp->hour, &pp->minute, &pp->second, - &wday, &pp->day, &month, &pp->year, &bst, &status); + &wday, &pp->day, &month, &pp->year, &flags, &status); /* Validate format and numbers. */ if(n != 9) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG /* Would expect to have caught major problems already... */ if(debug) { printf("arc: badly formatted data.\n"); } #endif + pp->lencode = 0; refclock_report(peer, CEVNT_BADREPLY); return; } @@ -1064,15 +1139,21 @@ arc_receive( (month < 1) || (month > 12) || (pp->year < 0) || (pp->year > 99)) { /* Data out of range. */ + pp->lencode = 0; refclock_report(peer, CEVNT_BADREPLY); return; } - /* Check that BST/UTC bits are the complement of one another. */ - if(!(bst & 2) == !(bst & 4)) { - refclock_report(peer, CEVNT_BADREPLY); - return; - } + + if(peer->MODE == 0) { /* compatiblity to original version */ + int bst = flags; + /* Check that BST/UTC bits are the complement of one another. */ + if(!(bst & 2) == !(bst & 4)) { + pp->lencode = 0; + refclock_report(peer, CEVNT_BADREPLY); + return; + } + } if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); } /* Year-2000 alert! */ @@ -1094,7 +1175,7 @@ arc_receive( printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n", n, pp->hour, pp->minute, pp->second, - pp->day, month, pp->year, bst, status); + pp->day, month, pp->year, flags, status); } #endif @@ -1114,41 +1195,159 @@ arc_receive( msyslog(LOG_NOTICE, "ARCRON: signal lost"); pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */ up->status = status; + pp->lencode = 0; refclock_report(peer, CEVNT_FAULT); return; } } up->status = status; - pp->day += moff[month - 1]; - - if(isleap_4(pp->year) && month > 2) { pp->day++; } /* Y2KFixes */ + if (peer->MODE == 0) { /* compatiblity to original version */ + int bst = flags; + + pp->day += moff[month - 1]; + + if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */ + + /* Convert to UTC if required */ + if(bst & 2) { + pp->hour--; + if (pp->hour < 0) { + pp->hour = 23; + pp->day--; + /* If we try to wrap round the year + * (BST on 1st Jan), reject.*/ + if(pp->day < 0) { + pp->lencode = 0; + refclock_report(peer, CEVNT_BADTIME); + return; + } + } + } + } - /* Convert to UTC if required */ - if(bst & 2) { - pp->hour--; - if (pp->hour < 0) { - pp->hour = 23; - pp->day--; - /* If we try to wrap round the year (BST on 1st Jan), reject.*/ - if(pp->day < 0) { - refclock_report(peer, CEVNT_BADTIME); + if(peer->MODE > 0) { + if(pp->sloppyclockflag & CLK_FLAG1) { + struct tm local; + struct tm *gmtp; + time_t unixtime; + + /* + * Convert to GMT for sites that distribute localtime. + * This means we have to do Y2K conversion on the + * 2-digit year; otherwise, we get the time wrong. + */ + + local.tm_year = pp->year-1900; + local.tm_mon = month-1; + local.tm_mday = pp->day; + local.tm_hour = pp->hour; + local.tm_min = pp->minute; + local.tm_sec = pp->second; + switch (peer->MODE) { + case 1: + local.tm_isdst = (flags & 2); + break; + case 2: + local.tm_isdst = (flags & 2); + break; + case 3: + switch (flags & 3) { + case 0: /* It is unclear exactly when the + Arcron changes from DST->ST and + ST->DST. Testing has shown this + to be irregular. For the time + being, let the OS decide. */ + local.tm_isdst = 0; +#ifdef DEBUG + if (debug) + printf ("arc: DST = 00 (0)\n"); +#endif + break; + case 1: /* dst->st time */ + local.tm_isdst = -1; +#ifdef DEBUG + if (debug) + printf ("arc: DST = 01 (1)\n"); +#endif + break; + case 2: /* st->dst time */ + local.tm_isdst = -1; +#ifdef DEBUG + if (debug) + printf ("arc: DST = 10 (2)\n"); +#endif + break; + case 3: /* dst time */ + local.tm_isdst = 1; +#ifdef DEBUG + if (debug) + printf ("arc: DST = 11 (3)\n"); +#endif + break; + } + break; + default: + msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", + peer->MODE); return; + break; + } + unixtime = mktime (&local); + if ((gmtp = gmtime (&unixtime)) == NULL) + { + pp->lencode = 0; + refclock_report (peer, CEVNT_FAULT); + return; + } + pp->year = gmtp->tm_year+1900; + month = gmtp->tm_mon+1; + pp->day = ymd2yd(pp->year,month,gmtp->tm_mday); + /* pp->day = gmtp->tm_yday; */ + pp->hour = gmtp->tm_hour; + pp->minute = gmtp->tm_min; + pp->second = gmtp->tm_sec; +#ifdef DEBUG + if (debug) + { + printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n", + pp->year,month,gmtp->tm_mday,pp->hour,pp->minute, + pp->second); } +#endif + } else + { + /* + * For more rational sites distributing UTC + */ + pp->day = ymd2yd(pp->year,month,pp->day); } } - /* If clock signal quality is unknown, revert to default PRECISION...*/ - if(up->quality == QUALITY_UNKNOWN) { peer->precision = PRECISION; } - /* ...else improve precision if flag3 is set... */ - else { - peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? - HIGHPRECISION : PRECISION); + if (peer->MODE == 0) { /* compatiblity to original version */ + /* If clock signal quality is + * unknown, revert to default PRECISION...*/ + if(up->quality == QUALITY_UNKNOWN) { + peer->precision = PRECISION; + } else { /* ...else improve precision if flag3 is set... */ + peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? + HIGHPRECISION : PRECISION); + } + } else { + if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) { + peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? + HIGHPRECISION : PRECISION); + } else if (up->quality == QUALITY_UNKNOWN) { + peer->precision = PRECISION; + } else { + peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ? + HIGHPRECISION : PRECISION); + } } /* Notice and log any change (eg from initial defaults) for flags. */ if(up->saved_flags != pp->sloppyclockflag) { -#ifdef ARCRON_DEBUG +#ifdef DEBUG msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s", ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."), ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."), @@ -1156,8 +1355,6 @@ arc_receive( ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : ".")); /* Note effects of flags changing... */ if(debug) { - printf("arc: CHOSENSAMPLES(pp) = %d.\n", CHOSENSAMPLES(pp)); - printf("arc: NKEEP(pp) = %d.\n", NKEEP(pp)); printf("arc: PRECISION = %d.\n", peer->precision); } #endif @@ -1185,9 +1382,11 @@ arc_receive( #endif if (!refclock_process(pp)) { + pp->lencode = 0; refclock_report(peer, CEVNT_BADTIME); return; } + record_clock_stats(&peer->srcadr, pp->a_lastcode); refclock_receive(peer); } @@ -1208,9 +1407,12 @@ request_time( if(debug) { printf("arc: unit %d: requesting time.\n", unit); } #endif if (!send_slow(up, pp->io.fd, "o\r")) { -#ifdef ARCRON_DEBUG - msyslog(LOG_NOTICE, "ARCRON: unit %d: problem sending", unit); +#ifdef DEBUG + if (debug) { + printf("arc: unit %d: problem sending", unit); + } #endif + pp->lencode = 0; refclock_report(peer, CEVNT_FAULT); return; } @@ -1232,8 +1434,10 @@ arc_poll( pp = peer->procptr; up = (struct arcunit *)pp->unitptr; +#if 0 pp->lencode = 0; memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode)); +#endif #if 0 /* Flush input. */ @@ -1241,7 +1445,8 @@ arc_poll( #endif /* Resync if our next scheduled resync time is here or has passed. */ - resync_needed = (up->next_resync <= current_time); + resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) && + (up->next_resync <= current_time) ); #ifdef ARCRON_LEAPSECOND_KEEN /* @@ -1309,6 +1514,7 @@ arc_poll( printf("arc: clock quality %d too poor.\n", up->quality); } #endif + pp->lencode = 0; refclock_report(peer, CEVNT_FAULT); return; }