Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/ntpd/refclock_arc.c
Line
Count
Source
1
/*
2
 * refclock_arc - clock driver for ARCRON MSF/DCF/WWVB receivers
3
 */
4
5
#ifdef HAVE_CONFIG_H
6
#include <config.h>
7
#endif
8
9
#include "ntp_types.h"
10
11
#if defined(REFCLOCK) && defined(CLOCK_ARCRON_MSF)
12
13
static const char arc_version[] = { "V1.3 2003/02/21" };
14
15
/* define PRE_NTP420 for compatibility to previous versions of NTP (at least
16
   to 4.1.0 */
17
#undef PRE_NTP420
18
19
#ifndef ARCRON_NOT_KEEN
20
#define ARCRON_KEEN 1 /* Be keen, and trusting of the clock, if defined. */
21
#endif
22
23
#ifndef ARCRON_NOT_MULTIPLE_SAMPLES
24
#define ARCRON_MULTIPLE_SAMPLES 1 /* Use all timestamp bytes as samples. */
25
#endif
26
27
#ifndef ARCRON_NOT_LEAPSECOND_KEEN
28
#ifndef ARCRON_LEAPSECOND_KEEN
29
#undef ARCRON_LEAPSECOND_KEEN /* Respond quickly to leap seconds: doesn't work yet. */
30
#endif
31
#endif
32
33
/*
34
Code by Derek Mulcahy, <derek@toybox.demon.co.uk>, 1997.
35
Modifications by Damon Hart-Davis, <d@hd.org>, 1997.
36
Modifications by Paul Alfille, <palfille@partners.org>, 2003.
37
Modifications by Christopher Price, <cprice@cs-home.com>, 2003.
38
Modifications by Nigel Roles <nigel@9fs.org>, 2003.
39
40
41
THIS CODE IS SUPPLIED AS IS, WITH NO WARRANTY OF ANY KIND.  USE AT
42
YOUR OWN RISK.
43
44
Orginally developed and used with ntp3-5.85 by Derek Mulcahy.
45
46
Built against ntp3-5.90 on Solaris 2.5 using gcc 2.7.2.
47
48
This code may be freely copied and used and incorporated in other
49
systems providing the disclaimer and notice of authorship are
50
reproduced.
51
52
-------------------------------------------------------------------------------
53
54
Nigel's notes:
55
56
1) Called tcgetattr() before modifying, so that fields correctly initialised
57
   for all operating systems
58
59
2) Altered parsing of timestamp line so that it copes with fields which are
60
   not always ASCII digits (e.g. status field when battery low)
61
62
-------------------------------------------------------------------------------
63
64
Christopher's notes:
65
66
MAJOR CHANGES SINCE V1.2 
67
========================
68
 1) Applied patch by Andrey Bray <abuse@madhouse.demon.co.uk>
69
    2001-02-17 comp.protocols.time.ntp
70
71
 2) Added WWVB support via clock mode command, localtime/UTC time configured
72
    via flag1=(0=UTC, 1=localtime)
73
74
 3) Added ignore resync request via flag2=(0=resync, 1=ignore resync)
75
76
 4) Added simplified conversion from localtime to UTC with dst/bst translation
77
78
 5) Added average signal quality poll
79
80
 6) Fixed a badformat error when no code is available due to stripping 
81
    \n & \r's 
82
83
 7) Fixed a badformat error when clearing lencode & memset a_lastcode in poll
84
    routine
85
86
 8) Lots of code cleanup, including standardized DEBUG macros and removal 
87
    of unused code 
88
89
-------------------------------------------------------------------------------
90
91
Author's original note:
92
93
I enclose my ntp driver for the Galleon Systems Arc MSF receiver.
94
95
It works (after a fashion) on both Solaris-1 and Solaris-2.
96
97
I am currently using ntp3-5.85.  I have been running the code for
98
about 7 months without any problems.  Even coped with the change to BST!
99
100
I had to do some funky things to read from the clock because it uses the
101
power from the receive lines to drive the transmit lines.  This makes the
102
code look a bit stupid but it works.  I also had to put in some delays to
103
allow for the turnaround time from receive to transmit.  These delays
104
are between characters when requesting a time stamp so that shouldn't affect
105
the results too drastically.
106
107
...
108
109
The bottom line is that it works but could easily be improved.  You are
110
free to do what you will with the code.  I haven't been able to determine
111
how good the clock is.  I think that this requires a known good clock
112
to compare it against.
113
114
-------------------------------------------------------------------------------
115
116
Damon's notes for adjustments:
117
118
MAJOR CHANGES SINCE V1.0
119
========================
120
 1) Removal of pollcnt variable that made the clock go permanently
121
    off-line once two time polls failed to gain responses.
122
123
 2) Avoiding (at least on Solaris-2) terminal becoming the controlling
124
    terminal of the process when we do a low-level open().
125
126
 3) Additional logic (conditional on ARCRON_LEAPSECOND_KEEN being
127
    defined) to try to resync quickly after a potential leap-second
128
    insertion or deletion.
129
130
 4) Code significantly slimmer at run-time than V1.0.
131
132
133
GENERAL
134
=======
135
136
 1) The C preprocessor symbol to have the clock built has been changed
137
    from ARC to ARCRON_MSF to CLOCK_ARCRON_MSF to minimise the
138
    possiblity of clashes with other symbols in the future.
139
140
 2) PRECISION should be -4/-5 (63ms/31ms) for the following reasons:
141
142
     a) The ARC documentation claims the internal clock is (only)
143
  accurate to about 20ms relative to Rugby (plus there must be
144
  noticable drift and delay in the ms range due to transmission
145
  delays and changing atmospheric effects).  This clock is not
146
  designed for ms accuracy as NTP has spoilt us all to expect.
147
148
     b) The clock oscillator looks like a simple uncompensated quartz
149
  crystal of the sort used in digital watches (ie 32768Hz) which
150
  can have large temperature coefficients and drifts; it is not
151
  clear if this oscillator is properly disciplined to the MSF
152
  transmission, but as the default is to resync only once per
153
  *day*, we can imagine that it is not, and is free-running.  We
154
  can minimise drift by resyncing more often (at the cost of
155
  reduced battery life), but drift/wander may still be
156
  significant.
157
158
     c) Note that the bit time of 3.3ms adds to the potential error in
159
  the the clock timestamp, since the bit clock of the serial link
160
  may effectively be free-running with respect to the host clock
161
  and the MSF clock.  Actually, the error is probably 1/16th of
162
  the above, since the input data is probably sampled at at least
163
  16x the bit rate.
164
165
    By keeping the clock marked as not very precise, it will have a
166
    fairly large dispersion, and thus will tend to be used as a
167
    `backup' time source and sanity checker, which this clock is
168
    probably ideal for.  For an isolated network without other time
169
    sources, this clock can probably be expected to provide *much*
170
    better than 1s accuracy, which will be fine.
171
172
    By default, PRECISION is set to -4, but experience, especially at a
173
    particular geographic location with a particular clock, may allow
174
    this to be altered to -5.  (Note that skews of +/- 10ms are to be
175
    expected from the clock from time-to-time.)  This improvement of
176
    reported precision can be instigated by setting flag3 to 1, though
177
    the PRECISION will revert to the normal value while the clock
178
    signal quality is unknown whatever the flag3 setting.
179
180
    IN ANY CASE, BE SURE TO SET AN APPROPRIATE FUDGE FACTOR TO REMOVE
181
    ANY RESIDUAL SKEW, eg:
182
183
  server 127.127.27.0 # ARCRON MSF radio clock unit 0.
184
  # Fudge timestamps by about 20ms.
185
  fudge 127.127.27.0 time1 0.020
186
187
    You will need to observe your system's behaviour, assuming you have
188
    some other NTP source to compare it with, to work out what the
189
    fudge factor should be.  For my Sun SS1 running SunOS 4.1.3_U1 with
190
    my MSF clock with my distance from the MSF transmitter, +20ms
191
    seemed about right, after some observation.
192
193
 3) REFID has been made "MSFa" to reflect the MSF time source and the
194
    ARCRON receiver.
195
196
 4) DEFAULT_RESYNC_TIME is the time in seconds (by default) before
197
    forcing a resync since the last attempt.  This is picked to give a
198
    little less than an hour between resyncs and to try to avoid
199
    clashing with any regular event at a regular time-past-the-hour
200
    which might cause systematic errors.
201
202
    The INITIAL_RESYNC_DELAY is to avoid bothering the clock and
203
    running down its batteries unnecesarily if ntpd is going to crash
204
    or be killed or reconfigured quickly.  If ARCRON_KEEN is defined
205
    then this period is long enough for (with normal polling rates)
206
    enough time samples to have been taken to allow ntpd to sync to
207
    the clock before the interruption for the clock to resync to MSF.
208
    This avoids ntpd syncing to another peer first and then
209
    almost immediately hopping to the MSF clock.
210
211
    The RETRY_RESYNC_TIME is used before rescheduling a resync after a
212
    resync failed to reveal a statisfatory signal quality (too low or
213
    unknown).
214
215
 5) The clock seems quite jittery, so I have increased the
216
    median-filter size from the typical (previous) value of 3.  I
217
    discard up to half the results in the filter.  It looks like maybe
218
    1 sample in 10 or so (maybe less) is a spike, so allow the median
219
    filter to discard at least 10% of its entries or 1 entry, whichever
220
    is greater.
221
222
 6) Sleeping *before* each character sent to the unit to allow required
223
    inter-character time but without introducting jitter and delay in
224
    handling the response if possible.
225
226
 7) If the flag ARCRON_KEEN is defined, take time samples whenever
227
    possible, even while resyncing, etc.  We rely, in this case, on the
228
    clock always giving us a reasonable time or else telling us in the
229
    status byte at the end of the timestamp that it failed to sync to
230
    MSF---thus we should never end up syncing to completely the wrong
231
    time.
232
233
 8) If the flag ARCRON_OWN_FILTER is defined, use own versions of
234
    refclock median-filter routines to get round small bug in 3-5.90
235
    code which does not return the median offset. XXX Removed this
236
    bit due NTP Version 4 upgrade - dlm.
237
238
 9) We would appear to have a year-2000 problem with this clock since
239
    it returns only the two least-significant digits of the year.  But
240
    ntpd ignores the year and uses the local-system year instead, so
241
    this is in fact not a problem.  Nevertheless, we attempt to do a
242
    sensible thing with the dates, wrapping them into a 100-year
243
    window.
244
245
 10)Logs stats information that can be used by Derek's Tcl/Tk utility
246
    to show the status of the clock.
247
248
 11)The clock documentation insists that the number of bits per
249
    character to be sent to the clock, and sent by it, is 11, including
250
    one start bit and two stop bits.  The data format is either 7+even
251
    or 8+none.
252
253
254
TO-DO LIST
255
==========
256
257
  * Eliminate use of scanf(), and maybe sprintf().
258
259
  * Allow user setting of resync interval to trade battery life for
260
    accuracy; maybe could be done via fudge factor or unit number.
261
262
  * Possibly note the time since the last resync of the MSF clock to
263
    MSF as the age of the last reference timestamp, ie trust the
264
    clock's oscillator not very much...
265
266
  * Add very slow auto-adjustment up to a value of +/- time2 to correct
267
    for long-term errors in the clock value (time2 defaults to 0 so the
268
    correction would be disabled by default).
269
270
  * Consider trying to use the tty_clk/ppsclock support.
271
272
  * Possibly use average or maximum signal quality reported during
273
    resync, rather than just the last one, which may be atypical.
274
275
*/
276
277
278
/* Notes for HKW Elektronik GmBH Radio clock driver */
279
/* Author Lyndon David, Sentinet Ltd, Feb 1997      */
280
/* These notes seem also to apply usefully to the ARCRON clock. */
281
282
/* The HKW clock module is a radio receiver tuned into the Rugby */
283
/* MSF time signal tranmitted on 60 kHz. The clock module connects */
284
/* to the computer via a serial line and transmits the time encoded */
285
/* in 15 bytes at 300 baud 7 bits two stop bits even parity */
286
287
/* Clock communications, from the datasheet */
288
/* All characters sent to the clock are echoed back to the controlling */
289
/* device. */
290
/* Transmit time/date information */
291
/* syntax ASCII o<cr> */
292
/* Character o may be replaced if neccesary by a character whose code */
293
/* contains the lowest four bits f(hex) eg */
294
/* syntax binary: xxxx1111 00001101 */
295
296
/* DHD note:
297
You have to wait for character echo + 10ms before sending next character.
298
*/
299
300
/* The clock replies to this command with a sequence of 15 characters */
301
/* which contain the complete time and a final <cr> making 16 characters */
302
/* in total. */
303
/* The RC computer clock will not reply immediately to this command because */
304
/* the start bit edge of the first reply character marks the beginning of */
305
/* the second. So the RC Computer Clock will reply to this command at the */
306
/* start of the next second */
307
/* The characters have the following meaning */
308
/* 1. hours tens   */
309
/* 2. hours units  */
310
/* 3. minutes tens */
311
/* 4. minutes units */
312
/* 5. seconds tens  */
313
/* 6. seconds units */
314
/* 7. day of week 1-monday 7-sunday */
315
/* 8. day of month tens */
316
/* 9. day of month units */
317
/* 10. month tens */
318
/* 11. month units */
319
/* 12. year tens */
320
/* 13. year units */
321
/* 14. BST/UTC status */
322
/*  bit 7 parity */
323
/*  bit 6 always 0 */
324
/*  bit 5 always 1 */
325
/*  bit 4 always 1 */
326
/*  bit 3 always 0 */
327
/*  bit 2 =1 if UTC is in effect, complementary to the BST bit */
328
/*  bit 1 =1 if BST is in effect, according to the BST bit     */
329
/*  bit 0 BST/UTC change impending bit=1 in case of change impending */
330
/* 15. status */
331
/*  bit 7 parity */
332
/*  bit 6 always 0 */
333
/*  bit 5 always 1 */
334
/*  bit 4 always 1 */
335
/*  bit 3 =1 if low battery is detected */
336
/*  bit 2 =1 if the very last reception attempt failed and a valid */
337
/*    time information already exists (bit0=1) */
338
/*    =0 if the last reception attempt was successful */
339
/*  bit 1 =1 if at least one reception since 2:30 am was successful */
340
/*    =0 if no reception attempt since 2:30 am was successful */
341
/*  bit 0 =1 if the RC Computer Clock contains valid time information */
342
/*    This bit is zero after reset and one after the first */
343
/*    successful reception attempt */
344
345
/* DHD note:
346
Also note g<cr> command which confirms that a resync is in progress, and
347
if so what signal quality (0--5) is available.
348
Also note h<cr> command which starts a resync to MSF signal.
349
*/
350
351
352
#include "ntpd.h"
353
#include "ntp_io.h"
354
#include "ntp_refclock.h"
355
#include "ntp_calendar.h"
356
#include "ntp_stdlib.h"
357
358
#include <stdio.h>
359
#include <ctype.h>
360
361
#if defined(HAVE_BSD_TTYS)
362
#include <sgtty.h>
363
#endif /* HAVE_BSD_TTYS */
364
365
#if defined(HAVE_SYSV_TTYS)
366
#include <termio.h>
367
#endif /* HAVE_SYSV_TTYS */
368
369
#if defined(HAVE_TERMIOS)
370
#include <termios.h>
371
#endif
372
373
/*
374
 * This driver supports the ARCRON MSF/DCF/WWVB Radio Controlled Clock
375
 */
376
377
/*
378
 * Interface definitions
379
 */
380
#define DEVICE    "/dev/arc%d"  /* Device name and unit. */
381
0
#define SPEED   B300    /* UART speed (300 baud) */
382
0
#define PRECISION (-4)    /* Precision  (~63 ms). */
383
0
#define HIGHPRECISION (-5)    /* If things are going well... */
384
0
#define REFID   "MSFa"    /* Reference ID. */
385
0
#define REFID_MSF "MSF"    /* Reference ID. */
386
0
#define REFID_DCF77 "DCF"    /* Reference ID. */
387
0
#define REFID_WWVB  "WWVB"    /* Reference ID. */
388
0
#define DESCRIPTION "ARCRON MSF/DCF/WWVB Receiver"
389
390
#ifdef PRE_NTP420
391
#define MODE ttlmax
392
#else
393
0
#define MODE ttl
394
#endif
395
396
0
#define LENARC    16    /* Format `o' timecode length. */
397
398
#define BITSPERCHAR 11    /* Bits per character. */
399
#define BITTIME   0x0DA740E /* Time for 1 bit at 300bps. */
400
#define CHARTIME10  0x8888888 /* Time for 10-bit char at 300bps. */
401
#define CHARTIME11  0x962FC96 /* Time for 11-bit char at 300bps. */
402
#define CHARTIME      /* Time for char at 300bps. */ \
403
( (BITSPERCHAR == 11) ? CHARTIME11 : ( (BITSPERCHAR == 10) ? CHARTIME10 : \
404
               (BITSPERCHAR * BITTIME) ) )
405
406
     /* Allow for UART to accept char half-way through final stop bit. */
407
#define INITIALOFFSET ((u_int32)(-BITTIME/2))
408
409
     /*
410
    charoffsets[x] is the time after the start of the second that byte
411
    x (with the first byte being byte 1) is received by the UART,
412
    assuming that the initial edge of the start bit of the first byte
413
    is on-time.  The values are represented as the fractional part of
414
    an l_fp.
415
416
    We store enough values to have the offset of each byte including
417
    the trailing \r, on the assumption that the bytes follow one
418
    another without gaps.
419
    */
420
     static const u_int32 charoffsets[LENARC+1] = {
421
#if BITSPERCHAR == 11 /* Usual case. */
422
       /* Offsets computed as accurately as possible... */
423
       0,
424
       INITIALOFFSET + 0x0962fc96, /*  1 chars,  11 bits */
425
       INITIALOFFSET + 0x12c5f92c, /*  2 chars,  22 bits */
426
       INITIALOFFSET + 0x1c28f5c3, /*  3 chars,  33 bits */
427
       INITIALOFFSET + 0x258bf259, /*  4 chars,  44 bits */
428
       INITIALOFFSET + 0x2eeeeeef, /*  5 chars,  55 bits */
429
       INITIALOFFSET + 0x3851eb85, /*  6 chars,  66 bits */
430
       INITIALOFFSET + 0x41b4e81b, /*  7 chars,  77 bits */
431
       INITIALOFFSET + 0x4b17e4b1, /*  8 chars,  88 bits */
432
       INITIALOFFSET + 0x547ae148, /*  9 chars,  99 bits */
433
       INITIALOFFSET + 0x5dddddde, /* 10 chars, 110 bits */
434
       INITIALOFFSET + 0x6740da74, /* 11 chars, 121 bits */
435
       INITIALOFFSET + 0x70a3d70a, /* 12 chars, 132 bits */
436
       INITIALOFFSET + 0x7a06d3a0, /* 13 chars, 143 bits */
437
       INITIALOFFSET + 0x8369d037, /* 14 chars, 154 bits */
438
       INITIALOFFSET + 0x8ccccccd, /* 15 chars, 165 bits */
439
       INITIALOFFSET + 0x962fc963  /* 16 chars, 176 bits */
440
#else
441
       /* Offsets computed with a small rounding error... */
442
       0,
443
       INITIALOFFSET +  1 * CHARTIME,
444
       INITIALOFFSET +  2 * CHARTIME,
445
       INITIALOFFSET +  3 * CHARTIME,
446
       INITIALOFFSET +  4 * CHARTIME,
447
       INITIALOFFSET +  5 * CHARTIME,
448
       INITIALOFFSET +  6 * CHARTIME,
449
       INITIALOFFSET +  7 * CHARTIME,
450
       INITIALOFFSET +  8 * CHARTIME,
451
       INITIALOFFSET +  9 * CHARTIME,
452
       INITIALOFFSET + 10 * CHARTIME,
453
       INITIALOFFSET + 11 * CHARTIME,
454
       INITIALOFFSET + 12 * CHARTIME,
455
       INITIALOFFSET + 13 * CHARTIME,
456
       INITIALOFFSET + 14 * CHARTIME,
457
       INITIALOFFSET + 15 * CHARTIME,
458
       INITIALOFFSET + 16 * CHARTIME
459
#endif
460
     };
461
462
0
#define DEFAULT_RESYNC_TIME  (57*60)  /* Gap between resync attempts (s). */
463
0
#define RETRY_RESYNC_TIME    (27*60)  /* Gap to emergency resync attempt. */
464
#ifdef ARCRON_KEEN
465
0
#define INITIAL_RESYNC_DELAY 500  /* Delay before first resync. */
466
#else
467
#define INITIAL_RESYNC_DELAY 50   /* Delay before first resync. */
468
#endif
469
470
     static const int moff[12] =
471
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
472
/* Flags for a raw open() of the clock serial device. */
473
#ifdef O_NOCTTY /* Good, we can avoid tty becoming controlling tty. */
474
#define OPEN_FLAGS (O_RDWR | O_NOCTTY)
475
#else   /* Oh well, it may not matter... */
476
#define OPEN_FLAGS (O_RDWR)
477
#endif
478
479
480
/* Length of queue of command bytes to be sent. */
481
0
#define CMDQUEUELEN 4      /* Enough for two cmds + each \r. */
482
/* Queue tick time; interval in seconds between chars taken off queue. */
483
/* Must be >= 2 to allow o\r response to come back uninterrupted. */
484
0
#define QUEUETICK   2      /* Allow o\r reply to finish. */
485
486
/*
487
 * ARC unit control structure
488
 */
489
struct arcunit {
490
  l_fp lastrec;     /* Time tag for the receive time (system). */
491
  int status;     /* Clock status. */
492
493
  int quality;      /* Quality of reception 0--5 for unit. */
494
  /* We may also use the values -1 or 6 internally. */
495
  u_long quality_stamp; /* Next time to reset quality average. */
496
497
  u_long next_resync; /* Next resync time (s) compared to current_time. */
498
  int resyncing;      /* Resync in progress if true. */
499
500
  /* In the outgoing queue, cmdqueue[0] is next to be sent. */
501
  char cmdqueue[CMDQUEUELEN+1]; /* Queue of outgoing commands + \0. */
502
503
  u_long saved_flags; /* Saved fudge flags. */
504
};
505
506
#ifdef ARCRON_LEAPSECOND_KEEN
507
/* The flag `possible_leap' is set non-zero when any MSF unit
508
       thinks a leap-second may have happened.
509
510
       Set whenever we receive a valid time sample in the first hour of
511
       the first day of the first/seventh months.
512
513
       Outside the special hour this value is unconditionally set
514
       to zero by the receive routine.
515
516
       On finding itself in this timeslot, as long as the value is
517
       non-negative, the receive routine sets it to a positive value to
518
       indicate a resync to MSF should be performed.
519
520
       In the poll routine, if this value is positive and we are not
521
       already resyncing (eg from a sync that started just before
522
       midnight), start resyncing and set this value negative to
523
       indicate that a leap-triggered resync has been started.  Having
524
       set this negative prevents the receive routine setting it
525
       positive and thus prevents multiple resyncs during the witching
526
       hour.
527
     */
528
static int possible_leap = 0;       /* No resync required by default. */
529
#endif
530
531
#if 0
532
static void dummy_event_handler (struct peer *);
533
static void   arc_event_handler (struct peer *);
534
#endif /* 0 */
535
536
0
#define QUALITY_UNKNOWN     -1 /* Indicates unknown clock quality. */
537
#define MIN_CLOCK_QUALITY    0 /* Min quality clock will return. */
538
0
#define MIN_CLOCK_QUALITY_OK 3 /* Min quality for OK reception. */
539
0
#define MAX_CLOCK_QUALITY    5 /* Max quality clock will return. */
540
541
/*
542
 * Function prototypes
543
 */
544
static  int arc_start (int, struct peer *);
545
static  void  arc_shutdown  (int, struct peer *);
546
static  void  arc_receive (struct recvbuf *);
547
static  void  arc_poll  (int, struct peer *);
548
549
/*
550
 * Transfer vector
551
 */
552
struct  refclock refclock_arc = {
553
  arc_start,    /* start up driver */
554
  arc_shutdown,   /* shut down driver */
555
  arc_poll,   /* transmit poll message */
556
  noentry,    /* not used (old arc_control) */
557
  noentry,    /* initialize driver (not used) */
558
  noentry,    /* not used (old arc_buginfo) */
559
  NOFLAGS     /* not used */
560
};
561
562
/* Queue us up for the next tick. */
563
#define ENQUEUE(up) \
564
0
  do { \
565
0
       peer->procptr->nextaction = current_time + QUEUETICK; \
566
0
  } while(0)
567
568
/* Placeholder event handler---does nothing safely---soaks up loose tick. */
569
static void
570
dummy_event_handler(
571
  struct peer *peer
572
  )
573
0
{
574
0
#ifdef DEBUG
575
0
  if(debug) { printf("arc: dummy_event_handler() called.\n"); }
576
0
#endif
577
0
}
578
579
/*
580
Normal event handler.
581
582
Take first character off queue and send to clock if not a null.
583
584
Shift characters down and put a null on the end.
585
586
We assume that there is no parallelism so no race condition, but even
587
if there is nothing bad will happen except that we might send some bad
588
data to the clock once in a while.
589
*/
590
static void
591
arc_event_handler(
592
  struct peer *peer
593
  )
594
0
{
595
0
  struct refclockproc *pp = peer->procptr;
596
0
  register struct arcunit *up = pp->unitptr;
597
0
  int i;
598
0
  char c;
599
0
#ifdef DEBUG
600
0
  if(debug > 2) { printf("arc: arc_event_handler() called.\n"); }
601
0
#endif
602
603
0
  c = up->cmdqueue[0];       /* Next char to be sent. */
604
  /* Shift down characters, shifting trailing \0 in at end. */
605
0
  for(i = 0; i < CMDQUEUELEN; ++i)
606
0
  { up->cmdqueue[i] = up->cmdqueue[i+1]; }
607
608
  /* Don't send '\0' characters. */
609
0
  if(c != '\0') {
610
0
    if(write(pp->io.fd, &c, 1) != 1) {
611
0
      msyslog(LOG_NOTICE, "ARCRON: write to fd %d failed", pp->io.fd);
612
0
    }
613
0
#ifdef DEBUG
614
0
    else if(debug) { printf("arc: sent `%2.2x', fd %d.\n", c, pp->io.fd); }
615
0
#endif
616
0
  }
617
618
0
  ENQUEUE(up);
619
0
}
620
621
/*
622
 * arc_start - open the devices and initialize data for processing
623
 */
624
static int
625
arc_start(
626
  int unit,
627
  struct peer *peer
628
  )
629
0
{
630
0
  register struct arcunit *up;
631
0
  struct refclockproc *pp;
632
0
  int temp_fd;
633
0
  int fd;
634
0
  char device[20];
635
0
#ifdef HAVE_TERMIOS
636
0
  struct termios arg;
637
0
#endif
638
639
0
  msyslog(LOG_NOTICE, "MSF_ARCRON %s: opening unit %d",
640
0
    arc_version, unit);
641
0
  DPRINTF(1, ("arc: %s: attempt to open unit %d.\n", arc_version,
642
0
    unit));
643
644
  /*
645
   * Open serial port. Use CLK line discipline, if available.
646
   */
647
0
  snprintf(device, sizeof(device), DEVICE, unit);
648
0
  temp_fd = refclock_open(&peer->srcadr, device, SPEED, LDISC_CLK);
649
0
  if (temp_fd <= 0)
650
0
    return 0;
651
0
  DPRINTF(1, ("arc: unit %d using tty_open().\n", unit));
652
0
  fd = tty_open(device, OPEN_FLAGS, 0777);
653
0
  if (fd < 0) {
654
0
    msyslog(LOG_ERR, "MSF_ARCRON(%d): failed second open(%s, 0777): %m.",
655
0
      unit, device);
656
0
    close(temp_fd);
657
0
    return 0;
658
0
  }
659
0
  close(temp_fd);
660
0
  temp_fd = -1;   /* not used after this, at *this* time. */
661
662
0
#ifndef SYS_WINNT
663
0
  if (-1 == fcntl(fd, F_SETFL, 0)) /* clear the descriptor flags */
664
0
    msyslog(LOG_ERR, "MSF_ARCRON(%d): fcntl(F_SETFL, 0): %m.",
665
0
      unit);
666
667
0
#endif
668
0
  DPRINTF(1, ("arc: opened RS232 port with file descriptor %d.\n", fd));
669
670
0
#ifdef HAVE_TERMIOS
671
672
0
  if (tcgetattr(fd, &arg) < 0) {
673
0
    msyslog(LOG_ERR, "MSF_ARCRON(%d): tcgetattr(%s): %m.",
674
0
      unit, device);
675
0
    close(fd);
676
0
    return 0;
677
0
  }
678
679
0
  arg.c_iflag = IGNBRK | ISTRIP;
680
0
  arg.c_oflag = 0;
681
0
  arg.c_cflag = B300 | CS8 | CREAD | CLOCAL | CSTOPB;
682
0
  arg.c_lflag = 0;
683
0
  arg.c_cc[VMIN] = 1;
684
0
  arg.c_cc[VTIME] = 0;
685
686
0
  if (tcsetattr(fd, TCSANOW, &arg) < 0) {
687
0
    msyslog(LOG_ERR, "MSF_ARCRON(%d): tcsetattr(%s): %m.",
688
0
      unit, device);
689
0
    close(fd);
690
0
    return 0;
691
0
  }
692
693
#else
694
695
  msyslog(LOG_ERR, "ARCRON: termios required by this driver");
696
  (void)close(fd);
697
698
  return 0;
699
700
#endif
701
702
  /* Set structure to all zeros... */
703
0
  up = emalloc_zero(sizeof(*up));
704
0
  pp = peer->procptr;
705
0
  pp->io.clock_recv = arc_receive;
706
0
  pp->io.srcclock = peer;
707
0
  pp->io.datalen = 0;
708
0
  pp->io.fd = fd;
709
0
  if (!io_addclock(&pp->io)) {
710
0
    close(fd);
711
0
    pp->io.fd = -1;
712
0
    free(up); 
713
0
    return(0); 
714
0
  }
715
0
  pp->unitptr = up;
716
717
  /*
718
   * Initialize miscellaneous variables
719
   */
720
0
  peer->precision = PRECISION;
721
0
  peer->stratum = 2;              /* Default to stratum 2 not 0. */
722
0
  pp->clockdesc = DESCRIPTION;
723
0
  if (peer->MODE > 3) {
724
0
    msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d", peer->MODE);
725
0
    return 0;
726
0
  }
727
0
#ifdef DEBUG
728
0
  if(debug) { printf("arc: mode = %d.\n", peer->MODE); }
729
0
#endif
730
0
  switch (peer->MODE) {
731
0
      case 1:
732
0
    memcpy((char *)&pp->refid, REFID_MSF, 4);
733
0
    break;
734
0
      case 2:
735
0
    memcpy((char *)&pp->refid, REFID_DCF77, 4);
736
0
    break;
737
0
      case 3:
738
0
    memcpy((char *)&pp->refid, REFID_WWVB, 4);
739
0
    break;
740
0
      default:
741
0
    memcpy((char *)&pp->refid, REFID, 4);
742
0
    break;
743
0
  }
744
  /* Spread out resyncs so that they should remain separated. */
745
0
  up->next_resync = current_time + INITIAL_RESYNC_DELAY + (67*unit)%1009;
746
747
#if 0 /* Not needed because of zeroing of arcunit structure... */
748
  up->resyncing = 0;              /* Not resyncing yet. */
749
  up->saved_flags = 0;            /* Default is all flags off. */
750
  /* Clear send buffer out... */
751
  {
752
    int i;
753
    for(i = CMDQUEUELEN; i >= 0; --i) { up->cmdqueue[i] = '\0'; }
754
  }
755
#endif
756
757
0
#ifdef ARCRON_KEEN
758
0
  up->quality = QUALITY_UNKNOWN;  /* Trust the clock immediately. */
759
#else
760
  up->quality = MIN_CLOCK_QUALITY;/* Don't trust the clock yet. */
761
#endif
762
763
0
  peer->procptr->action = arc_event_handler;
764
765
0
  ENQUEUE(up);
766
767
0
  return(1);
768
0
}
769
770
771
/*
772
 * arc_shutdown - shut down the clock
773
 */
774
static void
775
arc_shutdown(
776
  int unit,
777
  struct peer *peer
778
  )
779
0
{
780
0
  register struct arcunit *up;
781
0
  struct refclockproc *pp;
782
783
0
  peer->procptr->action = dummy_event_handler;
784
785
0
  pp = peer->procptr;
786
0
  up = pp->unitptr;
787
0
  if (-1 != pp->io.fd)
788
0
    io_closeclock(&pp->io);
789
0
  if (NULL != up)
790
0
    free(up);
791
0
}
792
793
/*
794
Compute space left in output buffer.
795
*/
796
static int
797
space_left(
798
  register struct arcunit *up
799
  )
800
0
{
801
0
  int spaceleft;
802
803
  /* Compute space left in buffer after any pending output. */
804
0
  for(spaceleft = 0; spaceleft < CMDQUEUELEN; ++spaceleft)
805
0
  { if(up->cmdqueue[CMDQUEUELEN - 1 - spaceleft] != '\0') { break; } }
806
0
  return(spaceleft);
807
0
}
808
809
/*
810
Send command by copying into command buffer as far forward as possible,
811
after any pending output.
812
813
Indicate an error by returning 0 if there is not space for the command.
814
*/
815
static int
816
send_slow(
817
  register struct arcunit *up,
818
  int fd,
819
  const char *s
820
  )
821
0
{
822
0
  int sl = strlen(s);
823
0
  int spaceleft = space_left(up);
824
825
0
#ifdef DEBUG
826
0
  if(debug > 1) { printf("arc: spaceleft = %d.\n", spaceleft); }
827
0
#endif
828
0
  if(spaceleft < sl) { /* Should not normally happen... */
829
0
#ifdef DEBUG
830
0
    msyslog(LOG_NOTICE, "ARCRON: send-buffer overrun (%d/%d)",
831
0
      sl, spaceleft);
832
0
#endif
833
0
    return(0);      /* FAILED! */
834
0
  }
835
836
  /* Copy in the command to be sent. */
837
0
  while(*s && spaceleft > 0) { up->cmdqueue[CMDQUEUELEN - spaceleft--] = *s++; }
838
839
0
  return(1);
840
0
}
841
842
843
static int
844
get2(char *p, int *val)
845
0
{
846
0
  if (!isdigit((unsigned char)p[0]) || !isdigit((unsigned char)p[1])) return 0;
847
0
  *val = (p[0] - '0') * 10 + p[1] - '0';
848
0
  return 1;
849
0
}
850
851
static int
852
get1(char *p, int *val)
853
0
{
854
0
  if (!isdigit((unsigned char)p[0])) return 0;
855
0
  *val = p[0] - '0';
856
0
  return 1;
857
0
}
858
859
/* Macro indicating action we will take for different quality values. */
860
0
#define quality_action(q) \
861
0
(((q) == QUALITY_UNKNOWN) ?         "UNKNOWN, will use clock anyway" : \
862
0
 (((q) < MIN_CLOCK_QUALITY_OK) ? "TOO POOR, will not use clock" : \
863
0
  "OK, will use clock"))
864
865
/*
866
 * arc_receive - receive data from the serial interface
867
 */
868
static void
869
arc_receive(
870
  struct recvbuf *rbufp
871
  )
872
0
{
873
0
  static int quality_average = 0;
874
0
  static int quality_sum = 0;
875
0
  static int quality_polls = 0;
876
0
  register struct arcunit *up;
877
0
  struct refclockproc *pp;
878
0
  struct peer *peer;
879
0
  char c;
880
0
  int i, wday, month, flags, status;
881
0
  int arc_last_offset;
882
0
    #ifdef DEBUG
883
0
  int n;
884
0
    #endif
885
886
  /*
887
   * Initialize pointers and read the timecode and timestamp
888
   */
889
0
  peer = rbufp->recv_peer;
890
0
  pp = peer->procptr;
891
0
  up = pp->unitptr;
892
893
894
  /*
895
    If the command buffer is empty, and we are resyncing, insert a
896
    g\r quality request into it to poll for signal quality again.
897
  */
898
0
  if((up->resyncing) && (space_left(up) == CMDQUEUELEN)) {
899
0
#ifdef DEBUG
900
0
    if(debug > 1) { printf("arc: inserting signal-quality poll.\n"); }
901
0
#endif
902
0
    send_slow(up, pp->io.fd, "g\r");
903
0
  }
904
905
  /*
906
    The `arc_last_offset' is the offset in lastcode[] of the last byte
907
    received, and which we assume actually received the input
908
    timestamp.
909
910
    (When we get round to using tty_clk and it is available, we
911
    assume that we will receive the whole timecode with the
912
    trailing \r, and that that \r will be timestamped.  But this
913
    assumption also works if receive the characters one-by-one.)
914
  */
915
0
  arc_last_offset = pp->lencode+rbufp->recv_length - 1;
916
917
  /*
918
    We catch a timestamp iff:
919
920
    * The command code is `o' for a timestamp.
921
922
    * If ARCRON_MULTIPLE_SAMPLES is undefined then we must have
923
    exactly char in the buffer (the command code) so that we
924
    only sample the first character of the timecode as our
925
    `on-time' character.
926
927
    * The first character in the buffer is not the echoed `\r'
928
    from the `o` command (so if we are to timestamp an `\r' it
929
    must not be first in the receive buffer with lencode==1.
930
    (Even if we had other characters following it, we probably
931
    would have a premature timestamp on the '\r'.)
932
933
    * We have received at least one character (I cannot imagine
934
    how it could be otherwise, but anyway...).
935
  */
936
0
  c = rbufp->recv_buffer[0];
937
0
  if((pp->a_lastcode[0] == 'o') &&
938
#ifndef ARCRON_MULTIPLE_SAMPLES
939
     (pp->lencode == 1) &&
940
#endif
941
0
     ((pp->lencode != 1) || (c != '\r')) &&
942
0
     (arc_last_offset >= 1)) {
943
    /* Note that the timestamp should be corrected if >1 char rcvd. */
944
0
    l_fp timestamp;
945
0
    timestamp = rbufp->recv_time;
946
0
#ifdef DEBUG
947
0
    if(debug) { /* Show \r as `R', other non-printing char as `?'. */
948
0
      printf("arc: stamp -->%c<-- (%d chars rcvd)\n",
949
0
             ((c == '\r') ? 'R' : (isgraph((unsigned char)c) ? c : '?')),
950
0
             rbufp->recv_length);
951
0
    }
952
0
#endif
953
954
    /*
955
      Now correct timestamp by offset of last byte received---we
956
      subtract from the receive time the delay implied by the
957
      extra characters received.
958
959
      Reject the input if the resulting code is too long, but
960
      allow for the trailing \r, normally not used but a good
961
      handle for tty_clk or somesuch kernel timestamper.
962
    */
963
0
    if(arc_last_offset > LENARC) {
964
0
#ifdef DEBUG
965
0
      if(debug) {
966
0
        printf("arc: input code too long (%d cf %d); rejected.\n",
967
0
               arc_last_offset, LENARC);
968
0
      }
969
0
#endif
970
0
      pp->lencode = 0;
971
0
      refclock_report(peer, CEVNT_BADREPLY);
972
0
      return;
973
0
    }
974
975
0
    L_SUBUF(&timestamp, charoffsets[arc_last_offset]);
976
0
#ifdef DEBUG
977
0
    if(debug > 1) {
978
0
      printf(
979
0
        "arc: %s%d char(s) rcvd, the last for lastcode[%d]; -%sms offset applied.\n",
980
0
        ((rbufp->recv_length > 1) ? "*** " : ""),
981
0
        rbufp->recv_length,
982
0
        arc_last_offset,
983
0
        mfptoms((unsigned long)0,
984
0
          charoffsets[arc_last_offset],
985
0
          1));
986
0
    }
987
0
#endif
988
989
0
#ifdef ARCRON_MULTIPLE_SAMPLES
990
    /*
991
      If taking multiple samples, capture the current adjusted
992
      sample iff:
993
994
      * No timestamp has yet been captured (it is zero), OR
995
996
      * This adjusted timestamp is earlier than the one already
997
      captured, on the grounds that this one suffered less
998
      delay in being delivered to us and is more accurate.
999
1000
    */
1001
0
    if(L_ISZERO(&(up->lastrec)) ||
1002
0
       L_ISGEQ(&(up->lastrec), &timestamp))
1003
0
#endif
1004
0
    {
1005
0
#ifdef DEBUG
1006
0
      if(debug > 1) {
1007
0
        printf("arc: system timestamp captured.\n");
1008
0
#ifdef ARCRON_MULTIPLE_SAMPLES
1009
0
        if(!L_ISZERO(&(up->lastrec))) {
1010
0
          l_fp diff;
1011
0
          diff = up->lastrec;
1012
0
          L_SUB(&diff, &timestamp);
1013
0
          printf("arc: adjusted timestamp by -%sms.\n",
1014
0
                 mfptoms(diff.l_ui, diff.l_uf, 3));
1015
0
        }
1016
0
#endif
1017
0
      }
1018
0
#endif
1019
0
      up->lastrec = timestamp;
1020
0
    }
1021
1022
0
  }
1023
1024
  /* Just in case we still have lots of rubbish in the buffer... */
1025
  /* ...and to avoid the same timestamp being reused by mistake, */
1026
  /* eg on receipt of the \r coming in on its own after the      */
1027
  /* timecode.                   */
1028
0
  if(pp->lencode >= LENARC) {
1029
0
#ifdef DEBUG
1030
0
    if(debug && (rbufp->recv_buffer[0] != '\r'))
1031
0
    { printf("arc: rubbish in pp->a_lastcode[].\n"); }
1032
0
#endif
1033
0
    pp->lencode = 0;
1034
0
    return;
1035
0
  }
1036
1037
  /* Append input to code buffer, avoiding overflow. */
1038
0
  for(i = 0; i < rbufp->recv_length; i++) {
1039
0
    if(pp->lencode >= LENARC) { break; } /* Avoid overflow... */
1040
0
    c = rbufp->recv_buffer[i];
1041
1042
    /* Drop trailing '\r's and drop `h' command echo totally. */
1043
0
    if(c != '\r' && c != 'h') { pp->a_lastcode[pp->lencode++] = c; }
1044
1045
    /*
1046
      If we've just put an `o' in the lastcode[0], clear the
1047
      timestamp in anticipation of a timecode arriving soon.
1048
1049
      We would expect to get to process this before any of the
1050
      timecode arrives.
1051
    */
1052
0
    if((c == 'o') && (pp->lencode == 1)) {
1053
0
      L_CLR(&(up->lastrec));
1054
0
#ifdef DEBUG
1055
0
      if(debug > 1) { printf("arc: clearing timestamp.\n"); }
1056
0
#endif
1057
0
    }
1058
0
  }
1059
0
  if (pp->lencode == 0) return;
1060
1061
  /* Handle a quality message. */
1062
0
  if(pp->a_lastcode[0] == 'g') {
1063
0
    int r, q;
1064
1065
0
    if(pp->lencode < 3) { return; } /* Need more data... */
1066
0
    r = (pp->a_lastcode[1] & 0x7f); /* Strip parity. */
1067
0
    q = (pp->a_lastcode[2] & 0x7f); /* Strip parity. */
1068
0
    if(((q & 0x70) != 0x30) || ((q & 0xf) > MAX_CLOCK_QUALITY) ||
1069
0
       ((r & 0x70) != 0x30)) {
1070
      /* Badly formatted response. */
1071
0
#ifdef DEBUG
1072
0
      if(debug) { printf("arc: bad `g' response %2x %2x.\n", r, q); }
1073
0
#endif
1074
0
      return;
1075
0
    }
1076
0
    if(r == '3') { /* Only use quality value whilst sync in progress. */
1077
0
      if (up->quality_stamp < current_time) {
1078
0
        struct calendar cal;
1079
0
        l_fp new_stamp;
1080
      
1081
0
        get_systime (&new_stamp);
1082
0
        caljulian (new_stamp.l_ui, &cal);
1083
0
        up->quality_stamp = 
1084
0
          current_time + 60 - cal.second + 5;
1085
0
        quality_sum = 0;
1086
0
        quality_polls = 0;
1087
0
      }
1088
0
      quality_sum += (q & 0xf);
1089
0
      quality_polls++;
1090
0
      quality_average = (quality_sum / quality_polls);
1091
0
#ifdef DEBUG
1092
0
      if(debug) { printf("arc: signal quality %d (%d).\n", quality_average, (q & 0xf)); }
1093
0
#endif
1094
0
    } else if( /* (r == '2') && */ up->resyncing) {
1095
0
      up->quality = quality_average;
1096
0
#ifdef DEBUG
1097
0
      if(debug)
1098
0
      {
1099
0
        printf("arc: sync finished, signal quality %d: %s\n",
1100
0
               up->quality,
1101
0
               quality_action(up->quality));
1102
0
      }
1103
0
#endif
1104
0
      msyslog(LOG_NOTICE,
1105
0
        "ARCRON: sync finished, signal quality %d: %s",
1106
0
        up->quality,
1107
0
        quality_action(up->quality));
1108
0
      up->resyncing = 0; /* Resync is over. */
1109
0
      quality_average = 0;
1110
0
      quality_sum = 0;
1111
0
      quality_polls = 0;
1112
1113
0
#ifdef ARCRON_KEEN
1114
      /* Clock quality dubious; resync earlier than usual. */
1115
0
      if((up->quality == QUALITY_UNKNOWN) ||
1116
0
         (up->quality < MIN_CLOCK_QUALITY_OK))
1117
0
      { up->next_resync = current_time + RETRY_RESYNC_TIME; }
1118
0
#endif
1119
0
    }
1120
0
    pp->lencode = 0;
1121
0
    return;
1122
0
  }
1123
1124
  /* Stop now if this is not a timecode message. */
1125
0
  if(pp->a_lastcode[0] != 'o') {
1126
0
    pp->lencode = 0;
1127
0
    refclock_report(peer, CEVNT_BADREPLY);
1128
0
    return;
1129
0
  }
1130
1131
  /* If we don't have enough data, wait for more... */
1132
0
  if(pp->lencode < LENARC) { return; }
1133
1134
1135
  /* WE HAVE NOW COLLECTED ONE TIMESTAMP (phew)... */
1136
0
#ifdef DEBUG
1137
0
  if(debug > 1) { printf("arc: NOW HAVE TIMESTAMP...\n"); }
1138
0
#endif
1139
1140
  /* But check that we actually captured a system timestamp on it. */
1141
0
  if(L_ISZERO(&(up->lastrec))) {
1142
0
#ifdef DEBUG
1143
0
    if(debug) { printf("arc: FAILED TO GET SYSTEM TIMESTAMP\n"); }
1144
0
#endif
1145
0
    pp->lencode = 0;
1146
0
    refclock_report(peer, CEVNT_BADREPLY);
1147
0
    return;
1148
0
  }
1149
  /*
1150
    Append a mark of the clock's received signal quality for the
1151
    benefit of Derek Mulcahy's Tcl/Tk utility (we map the `unknown'
1152
    quality value to `6' for his s/w) and terminate the string for
1153
    sure.  This should not go off the buffer end.
1154
  */
1155
0
  pp->a_lastcode[pp->lencode] = ((up->quality == QUALITY_UNKNOWN) ?
1156
0
               '6' : ('0' + up->quality));
1157
0
  pp->a_lastcode[pp->lencode + 1] = '\0'; /* Terminate for printf(). */
1158
1159
#ifdef PRE_NTP420
1160
  /* We don't use the micro-/milli- second part... */
1161
  pp->usec = 0;
1162
  pp->msec = 0;
1163
#else
1164
  /* We don't use the nano-second part... */
1165
0
  pp->nsec = 0;
1166
0
#endif  
1167
  /* Validate format and numbers. */
1168
0
  if (pp->a_lastcode[0] != 'o'
1169
0
    || !get2(pp->a_lastcode + 1, &pp->hour)
1170
0
    || !get2(pp->a_lastcode + 3, &pp->minute)
1171
0
    || !get2(pp->a_lastcode + 5, &pp->second)
1172
0
    || !get1(pp->a_lastcode + 7, &wday)
1173
0
    || !get2(pp->a_lastcode + 8, &pp->day)
1174
0
    || !get2(pp->a_lastcode + 10, &month)
1175
0
    || !get2(pp->a_lastcode + 12, &pp->year)) {
1176
0
#ifdef DEBUG
1177
    /* Would expect to have caught major problems already... */
1178
0
    if(debug) { printf("arc: badly formatted data.\n"); }
1179
0
#endif
1180
0
    pp->lencode = 0;
1181
0
    refclock_report(peer, CEVNT_BADREPLY);
1182
0
    return;
1183
0
  }
1184
0
  flags = pp->a_lastcode[14];
1185
0
  status = pp->a_lastcode[15];
1186
0
#ifdef DEBUG
1187
0
  if(debug) { printf("arc: status 0x%.2x flags 0x%.2x\n", flags, status); }
1188
0
  n = 9;
1189
0
#endif
1190
1191
  /*
1192
    Validate received values at least enough to prevent internal
1193
    array-bounds problems, etc.
1194
  */
1195
0
  if((pp->hour < 0) || (pp->hour > 23) ||
1196
0
     (pp->minute < 0) || (pp->minute > 59) ||
1197
0
     (pp->second < 0) || (pp->second > 60) /*Allow for leap seconds.*/ ||
1198
0
     (wday < 1) || (wday > 7) ||
1199
0
     (pp->day < 1) || (pp->day > 31) ||
1200
0
     (month < 1) || (month > 12) ||
1201
0
     (pp->year < 0) || (pp->year > 99)) {
1202
    /* Data out of range. */
1203
0
    pp->lencode = 0;
1204
0
    refclock_report(peer, CEVNT_BADREPLY);
1205
0
    return;
1206
0
  }
1207
1208
1209
0
  if(peer->MODE == 0) { /* compatiblity to original version */
1210
0
    int bst = flags;
1211
    /* Check that BST/UTC bits are the complement of one another. */
1212
0
    if(!(bst & 2) == !(bst & 4)) {
1213
0
      pp->lencode = 0;
1214
0
      refclock_report(peer, CEVNT_BADREPLY);
1215
0
      return;
1216
0
    }
1217
0
  }
1218
0
  if(status & 0x8) { msyslog(LOG_NOTICE, "ARCRON: battery low"); }
1219
1220
  /* Year-2000 alert! */
1221
  /* Attempt to wrap 2-digit date into sensible window. */
1222
0
  if(pp->year < YEAR_PIVOT) { pp->year += 100; }    /* Y2KFixes */
1223
0
  pp->year += 1900; /* use full four-digit year */  /* Y2KFixes */
1224
  /*
1225
    Attempt to do the right thing by screaming that the code will
1226
    soon break when we get to the end of its useful life.  What a
1227
    hero I am...  PLEASE FIX LEAP-YEAR AND WRAP CODE IN 209X!
1228
  */
1229
0
  if(pp->year >= YEAR_PIVOT+2000-2 ) {       /* Y2KFixes */
1230
    /*This should get attention B^> */
1231
0
    msyslog(LOG_NOTICE,
1232
0
      "ARCRON: fix me!  EITHER YOUR DATE IS BADLY WRONG or else I will break soon!");
1233
0
  }
1234
0
#ifdef DEBUG
1235
0
  if(debug) {
1236
0
    printf("arc: n=%d %02d:%02d:%02d %02d/%02d/%04d %1d %1d\n",
1237
0
           n,
1238
0
           pp->hour, pp->minute, pp->second,
1239
0
           pp->day, month, pp->year, flags, status);
1240
0
  }
1241
0
#endif
1242
1243
  /*
1244
    The status value tested for is not strictly supported by the
1245
    clock spec since the value of bit 2 (0x4) is claimed to be
1246
    undefined for MSF, yet does seem to indicate if the last resync
1247
    was successful or not.
1248
  */
1249
0
  pp->leap = LEAP_NOWARNING;
1250
0
  status &= 0x7;
1251
0
  if(status == 0x3) {
1252
0
    if(status != up->status)
1253
0
    { msyslog(LOG_NOTICE, "ARCRON: signal acquired"); }
1254
0
  } else {
1255
0
    if(status != up->status) {
1256
0
      msyslog(LOG_NOTICE, "ARCRON: signal lost");
1257
0
      pp->leap = LEAP_NOTINSYNC; /* MSF clock is free-running. */
1258
0
      up->status = status;
1259
0
      pp->lencode = 0;
1260
0
      refclock_report(peer, CEVNT_FAULT);
1261
0
      return;
1262
0
    }
1263
0
  }
1264
0
  up->status = status;
1265
1266
0
  if (peer->MODE == 0) { /* compatiblity to original version */
1267
0
    int bst = flags;
1268
1269
0
    pp->day += moff[month - 1];
1270
1271
0
    if(isleap_4(pp->year) && month > 2) { pp->day++; }/* Y2KFixes */
1272
1273
    /* Convert to UTC if required */
1274
0
    if(bst & 2) {
1275
0
      pp->hour--;
1276
0
      if (pp->hour < 0) {
1277
0
        pp->hour = 23;
1278
0
        pp->day--;
1279
        /* If we try to wrap round the year
1280
         * (BST on 1st Jan), reject.*/
1281
0
        if(pp->day < 0) {
1282
0
          pp->lencode = 0;
1283
0
          refclock_report(peer, CEVNT_BADTIME);
1284
0
          return;
1285
0
        }
1286
0
      }
1287
0
    }
1288
0
  }
1289
1290
0
  if(peer->MODE > 0) {
1291
0
    if(pp->sloppyclockflag & CLK_FLAG1) {
1292
0
      struct tm  local;
1293
0
      struct tm *gmtp;
1294
0
      time_t     unixtime;
1295
1296
      /*
1297
       * Convert to GMT for sites that distribute localtime.
1298
       * This means we have to do Y2K conversion on the
1299
       * 2-digit year; otherwise, we get the time wrong.
1300
       */
1301
1302
0
      memset(&local, 0, sizeof(local));
1303
1304
0
      local.tm_year  = pp->year-1900;
1305
0
      local.tm_mon   = month-1;
1306
0
      local.tm_mday  = pp->day;
1307
0
      local.tm_hour  = pp->hour;
1308
0
      local.tm_min   = pp->minute;
1309
0
      local.tm_sec   = pp->second;
1310
0
      switch (peer->MODE) {
1311
0
          case 1:
1312
0
        local.tm_isdst = (flags & 2);
1313
0
        break;
1314
0
          case 2:
1315
0
        local.tm_isdst = (flags & 2);
1316
0
        break;
1317
0
          case 3:
1318
0
        switch (flags & 3) {
1319
0
            case 0: /* It is unclear exactly when the 
1320
                 Arcron changes from DST->ST and 
1321
                 ST->DST. Testing has shown this
1322
                 to be irregular. For the time 
1323
                 being, let the OS decide. */
1324
0
          local.tm_isdst = 0;
1325
0
#ifdef DEBUG
1326
0
          if (debug)
1327
0
              printf ("arc: DST = 00 (0)\n"); 
1328
0
#endif
1329
0
          break;
1330
0
            case 1: /* dst->st time */
1331
0
          local.tm_isdst = -1;
1332
0
#ifdef DEBUG
1333
0
          if (debug) 
1334
0
              printf ("arc: DST = 01 (1)\n"); 
1335
0
#endif
1336
0
          break;
1337
0
            case 2: /* st->dst time */
1338
0
          local.tm_isdst = -1;
1339
0
#ifdef DEBUG
1340
0
          if (debug) 
1341
0
              printf ("arc: DST = 10 (2)\n"); 
1342
0
#endif
1343
0
          break;
1344
0
            case 3: /* dst time */
1345
0
                local.tm_isdst = 1;
1346
0
#ifdef DEBUG
1347
0
          if (debug) 
1348
0
              printf ("arc: DST = 11 (3)\n"); 
1349
0
#endif
1350
0
          break;
1351
0
        }
1352
0
        break;
1353
0
          default:
1354
0
        msyslog(LOG_NOTICE, "ARCRON: Invalid mode %d",
1355
0
          peer->MODE);
1356
0
        return;
1357
0
        break;
1358
0
      }
1359
0
      unixtime = mktime (&local);
1360
0
      if ((gmtp = gmtime (&unixtime)) == NULL)
1361
0
      {
1362
0
        pp->lencode = 0;
1363
0
        refclock_report (peer, CEVNT_FAULT);
1364
0
        return;
1365
0
      }
1366
0
      pp->year = gmtp->tm_year+1900;
1367
0
      month = gmtp->tm_mon+1;
1368
0
      pp->day = ymd2yd(pp->year,month,gmtp->tm_mday);
1369
      /* pp->day = gmtp->tm_yday; */
1370
0
      pp->hour = gmtp->tm_hour;
1371
0
      pp->minute = gmtp->tm_min;
1372
0
      pp->second = gmtp->tm_sec;
1373
0
#ifdef DEBUG
1374
0
      if (debug)
1375
0
      {
1376
0
        printf ("arc: time is %04d/%02d/%02d %02d:%02d:%02d UTC\n",
1377
0
          pp->year,month,gmtp->tm_mday,pp->hour,pp->minute,
1378
0
          pp->second);
1379
0
      }
1380
0
#endif
1381
0
    } else 
1382
0
    {
1383
      /*
1384
      * For more rational sites distributing UTC
1385
      */
1386
0
      pp->day    = ymd2yd(pp->year,month,pp->day);
1387
0
    }
1388
0
  }
1389
1390
0
  if (peer->MODE == 0) { /* compatiblity to original version */
1391
        /* If clock signal quality is 
1392
         * unknown, revert to default PRECISION...*/
1393
0
    if(up->quality == QUALITY_UNKNOWN) { 
1394
0
      peer->precision = PRECISION; 
1395
0
    } else { /* ...else improve precision if flag3 is set... */
1396
0
      peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1397
0
             HIGHPRECISION : PRECISION);
1398
0
    }
1399
0
  } else {
1400
0
    if ((status == 0x3) && (pp->sloppyclockflag & CLK_FLAG2)) {
1401
0
      peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1402
0
             HIGHPRECISION : PRECISION);
1403
0
    } else if (up->quality == QUALITY_UNKNOWN) {
1404
0
      peer->precision = PRECISION;
1405
0
    } else {
1406
0
      peer->precision = ((pp->sloppyclockflag & CLK_FLAG3) ?
1407
0
             HIGHPRECISION : PRECISION);
1408
0
    }
1409
0
  }
1410
1411
  /* Notice and log any change (eg from initial defaults) for flags. */
1412
0
  if(up->saved_flags != pp->sloppyclockflag) {
1413
0
#ifdef DEBUG
1414
0
    msyslog(LOG_NOTICE, "ARCRON: flags enabled: %s%s%s%s",
1415
0
      ((pp->sloppyclockflag & CLK_FLAG1) ? "1" : "."),
1416
0
      ((pp->sloppyclockflag & CLK_FLAG2) ? "2" : "."),
1417
0
      ((pp->sloppyclockflag & CLK_FLAG3) ? "3" : "."),
1418
0
      ((pp->sloppyclockflag & CLK_FLAG4) ? "4" : "."));
1419
    /* Note effects of flags changing... */
1420
0
    if(debug) {
1421
0
      printf("arc: PRECISION = %d.\n", peer->precision);
1422
0
    }
1423
0
#endif
1424
0
    up->saved_flags = pp->sloppyclockflag;
1425
0
  }
1426
1427
  /* Note time of last believable timestamp. */
1428
0
  pp->lastrec = up->lastrec;
1429
1430
#ifdef ARCRON_LEAPSECOND_KEEN
1431
  /* Find out if a leap-second might just have happened...
1432
     (ie is this the first hour of the first day of Jan or Jul?)
1433
  */
1434
  if((pp->hour == 0) &&
1435
     (pp->day == 1) &&
1436
     ((month == 1) || (month == 7))) {
1437
    if(possible_leap >= 0) {
1438
      /* A leap may have happened, and no resync has started yet...*/
1439
      possible_leap = 1;
1440
    }
1441
  } else {
1442
    /* Definitely not leap-second territory... */
1443
    possible_leap = 0;
1444
  }
1445
#endif
1446
1447
0
  if (!refclock_process(pp)) {
1448
0
    pp->lencode = 0;
1449
0
    refclock_report(peer, CEVNT_BADTIME);
1450
0
    return;
1451
0
  }
1452
0
  record_clock_stats(&peer->srcadr, pp->a_lastcode);
1453
0
  refclock_receive(peer);
1454
0
}
1455
1456
1457
/* request_time() sends a time request to the clock with given peer. */
1458
/* This automatically reports a fault if necessary. */
1459
/* No data should be sent after this until arc_poll() returns. */
1460
static  void    request_time    (int, struct peer *);
1461
static void
1462
request_time(
1463
  int unit,
1464
  struct peer *peer
1465
  )
1466
0
{
1467
0
  struct refclockproc *pp = peer->procptr;
1468
0
  register struct arcunit *up = pp->unitptr;
1469
0
#ifdef DEBUG
1470
0
  if(debug) { printf("arc: unit %d: requesting time.\n", unit); }
1471
0
#endif
1472
0
  if (!send_slow(up, pp->io.fd, "o\r")) {
1473
0
#ifdef DEBUG
1474
0
    if (debug) {
1475
0
      printf("arc: unit %d: problem sending", unit);
1476
0
    }
1477
0
#endif
1478
0
    pp->lencode = 0;
1479
0
    refclock_report(peer, CEVNT_FAULT);
1480
0
    return;
1481
0
  }
1482
0
  pp->polls++;
1483
0
}
1484
1485
/*
1486
 * arc_poll - called by the transmit procedure
1487
 */
1488
static void
1489
arc_poll(
1490
  int unit,
1491
  struct peer *peer
1492
  )
1493
0
{
1494
0
  register struct arcunit *up;
1495
0
  struct refclockproc *pp;
1496
0
  int resync_needed;              /* Should we start a resync? */
1497
1498
0
  pp = peer->procptr;
1499
0
  up = pp->unitptr;
1500
#if 0
1501
  pp->lencode = 0;
1502
  memset(pp->a_lastcode, 0, sizeof(pp->a_lastcode));
1503
#endif
1504
1505
#if 0
1506
  /* Flush input. */
1507
  tcflush(pp->io.fd, TCIFLUSH);
1508
#endif
1509
1510
  /* Resync if our next scheduled resync time is here or has passed. */
1511
0
  resync_needed = ( !(pp->sloppyclockflag & CLK_FLAG2) &&
1512
0
        (up->next_resync <= current_time) );
1513
1514
#ifdef ARCRON_LEAPSECOND_KEEN
1515
  /*
1516
    Try to catch a potential leap-second insertion or deletion quickly.
1517
1518
    In addition to the normal NTP fun of clocks that don't report
1519
    leap-seconds spooking their hosts, this clock does not even
1520
    sample the radio sugnal the whole time, so may miss a
1521
    leap-second insertion or deletion for up to a whole sample
1522
    time.
1523
1524
    To try to minimise this effect, if in the first few minutes of
1525
    the day immediately following a leap-second-insertion point
1526
    (ie in the first hour of the first day of the first and sixth
1527
    months), and if the last resync was in the previous day, and a
1528
    resync is not already in progress, resync the clock
1529
    immediately.
1530
1531
  */
1532
  if((possible_leap > 0) &&       /* Must be 00:XX 01/0{1,7}/XXXX. */
1533
     (!up->resyncing)) {          /* No resync in progress yet. */
1534
    resync_needed = 1;
1535
    possible_leap = -1;          /* Prevent multiple resyncs. */
1536
    msyslog(LOG_NOTICE,"ARCRON: unit %d: checking for leap second",unit);
1537
  }
1538
#endif
1539
1540
  /* Do a resync if required... */
1541
0
  if(resync_needed) {
1542
    /* First, reset quality value to `unknown' so we can detect */
1543
    /* when a quality message has been responded to by this     */
1544
    /* being set to some other value.                           */
1545
0
    up->quality = QUALITY_UNKNOWN;
1546
1547
    /* Note that we are resyncing... */
1548
0
    up->resyncing = 1;
1549
1550
    /* Now actually send the resync command and an immediate poll. */
1551
0
#ifdef DEBUG
1552
0
    if(debug) { printf("arc: sending resync command (h\\r).\n"); }
1553
0
#endif
1554
0
    msyslog(LOG_NOTICE, "ARCRON: unit %d: sending resync command", unit);
1555
0
    send_slow(up, pp->io.fd, "h\r");
1556
1557
    /* Schedule our next resync... */
1558
0
    up->next_resync = current_time + DEFAULT_RESYNC_TIME;
1559
1560
    /* Drop through to request time if appropriate. */
1561
0
  }
1562
1563
  /* If clock quality is too poor to trust, indicate a fault. */
1564
  /* If quality is QUALITY_UNKNOWN and ARCRON_KEEN is defined,*/
1565
  /* we'll cross our fingers and just hope that the thing     */
1566
  /* synced so quickly we did not catch it---we'll            */
1567
  /* double-check the clock is OK elsewhere.                  */
1568
0
  if(
1569
0
#ifdef ARCRON_KEEN
1570
0
    (up->quality != QUALITY_UNKNOWN) &&
1571
#else
1572
    (up->quality == QUALITY_UNKNOWN) ||
1573
#endif
1574
0
    (up->quality < MIN_CLOCK_QUALITY_OK)) {
1575
0
#ifdef DEBUG
1576
0
    if(debug) {
1577
0
      printf("arc: clock quality %d too poor.\n", up->quality);
1578
0
    }
1579
0
#endif
1580
0
    pp->lencode = 0;
1581
0
    refclock_report(peer, CEVNT_FAULT);
1582
0
    return;
1583
0
  }
1584
  /* This is the normal case: request a timestamp. */
1585
0
  request_time(unit, peer);
1586
0
}
1587
1588
#else
1589
NONEMPTY_TRANSLATION_UNIT
1590
#endif