weAut_01 / weAutSys    R 2.2.1
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines
Files | Data Structures | Defines | Functions | Variables
NTP client
+ + Application (layer) support + +

Overview

At the upper stack levels uIP brings some protocol and application support. weAutSys adapted and uses some of those (DHCP, ARP, DNS) as well as implementing some other protocols (like NTP, Telnet server).

weAutSys' NTP client is best used with above said DHCP client, which can get one or two NTP server addresses. Any Windows or appropriate Linux server on the network segment can take the role of the time server for all (weAut_01) automation modules there.
DHCP and NTP may very well be on the same server machine.

As the recommended configuration is to have a separated private LAN segment for all automation modules plus their supporting central servers some of the complexities of the NTP protocol (with the RFC's permission) are dismissed.

Complexities dismissed are, for example, expensive filtering and selection algorithms which could deal with malicious servers among others. It is assumed Bycantine servers being kept away from above said automation LAN by other means. And of course, all principal limitations of NTP synchronisation, like unavoidable offsets due to network round trip and software time stamping asymmetries etc., do apply also here.

On the other hand weAutSys' NTP client implementation is not as simplistic as SNTP. It uses the obvious NTP difference formulas and sets the date / time (hard) for big differences. Relative times and timers are not affected by those settings ("jumps"), but functions and timers dependent on absolute time and date may be.

Such hard setting of date and time will occur once after reset or power up as soon as a connection to a NTP server is established.

Small differences will be caught up by slightly trimming the oscillator responsible for the milliseconds tick. As long as the network and above said NTP server behave well, the control strategy implemented will make this tick exact in the median. And it will avoid all further (hard) date / time settings. The only exception then remaining will necessarily be leap seconds and DST shifts.

Relative milliseconds and seconds timers will (in the median) also get the NTP servers accuracy. Depending on atomic time, NTP time and related frequency accuracy is better than weAut_01 quartz oscillator's — which is quite good by the way. But beware, there are reports of surprisingly bad (black sheep) NTP servers around — using (indirectly) on of them is worse than having none. Just check.


Files

file  ntp.h
 

weAutSys' system calls and types for network time services


Data Structures

struct  ntpMess_t
 NTP message type. More...
struct  ntpState_t
 Structure for NTP (client) application state. More...
struct  ntpTimestamp_t
 NTP time stamp type. More...

Defines

#define FLAGS_LI_MASK
 leap indicator bits in ntpMess_t::flags
#define FLAGS_MODE_MASK
 mode bits in ntpMess_t::flags
#define FLAGS_VERSION_MASK
 version bits in ntpMess_t::flags
#define LI_59SEC
 leap indicator 2 : minute = 59s
#define LI_61SEC
 leap indicator 1 : minute = 61s
#define LI_ALARM
 leap indicator 3 : alarm (clock not synchronized)
#define LI_NOWARN
 leap indicator 0 : no warning
#define NTP_ADJUSTAT_DIRMSK
 last adjustment direction mask
#define NTP_ADJUSTAT_MINUS
 last adjustment negative
#define NTP_ADJUSTAT_PLUS
 last adjustment positive
#define NTP_ADJUSTAT_PRV_MINUS
 previous (not last) adjustment negative
#define NTP_ADJUSTAT_PRV_MSK
 previous (not last) adjustment mask
#define NTP_ADJUSTAT_PRV_PLUS
 lprevious (not last) adjustment positive
#define NTP_ADJUSTAT_RNG_1S
 last adjustment range +1 second
#define NTP_ADJUSTAT_RNG_HM
 last adjustment range milliseconds
#define NTP_ADJUSTAT_RNG_HS
 last adjustment range seconds
#define NTP_ADJUSTAT_RNG_LM
 last adjustment range low milliseconds
#define NTP_ADJUSTAT_RNGMSK
 last adjustment range mask
#define NTP_BROADCAST
 NTP mode broadcast (5)
#define NTP_CLIENT
 NTP mode client (3)
#define NTP_FRACT_DROP
 Fraction byte significance.
#define NTP_PORT
 The well known NTP port.
#define NTP_SERVER
 NTP mode server (4)
#define NTP_STATE_CNETAB   0xE0
 connection established
#define NTP_STATE_CNPROB   0xF0
 connection problematic
#define NTP_STATE_CONN1   1
 Server known, port connected to server 1.
#define NTP_STATE_CONN2   2
 Server known, port connected to server 2.
#define NTP_STATE_CONTSY   0xC0
 continuous synchronisation
#define NTP_STATE_OPMSK
 Operation (bits) mask.
#define NTP_STATE_RESET   0
 No known NTP server, no NTP client operation.
#define NTP_STATE_SEPROB   0xB0
 server answer problematic (bogus, alarm)
#define NTP_STATE_SRVMSK
 Server (bits) mask.
#define NTP_STATE_TRYREQ   0x10
 try (first) request
#define NTP_STATE_W4REPL   8
 flag: waiting for server's reply
#define NTP_VERSION
 current version (4) within FLAGS_VERSION_MASK
#define ntpAsksPrio()
 NTP asks for priority.

Functions

void adjustNTPtime308UTC (struct ntpTimestamp_t *ntpTimeDif)
 Adjust the actual (system) time by a NTP time stamp difference.
ptfnct_t ntpAppcall (void)
 Handle NTP (server) events.
void ntpInit (uint8_t pref2)
 Initialise the NTP (client)
void ntpReset (void)
 Reset the NTP (client) to initial state.
void ntpTimestampDif (struct ntpTimestamp_t *result, struct ntpTimestamp_t *a, struct ntpTimestamp_t *b)
 Difference of two NTP time stamps.
void ntpTimestampHalf (struct ntpTimestamp_t *result) __attribute__((pure))
 Half a NTP time stamp value.
void ntpTimestampSum (struct ntpTimestamp_t *result, struct ntpTimestamp_t *a, struct ntpTimestamp_t *b)
 Sum of two NTP time stamps.
void ntpTimestampToMillies (uint16_t *ms, struct ntpTimestamp_t *ntpStamp)
 Milliseconds from a NTP time stamp's fraction.
void setNTPstampAct (struct ntpTimestamp_t *ntpTimestamp)
 Set a NTP time stamp to actual time.

Variables

struct ntpState_t ntpState
 the NTP state

Define Documentation

#define NTP_PORT

The well known NTP port.

NTP normally uses UDP port 123.

#define NTP_FRACT_DROP

Fraction byte significance.

As said NTP time stamp's fraction has four bytes allowing 2**-32 = ~0.23ns resolution. That is far beyond any practical use for e.g. ATmel ATmega1284P based automation modules, like weAut_01 with 20MHz processor clock (50ns period) at best.

Hence this value determines the number of bytes being ignored for all 64 bit NTP time stamp operations.

Range: 0 (full 64 bit arithmetic) .. 3 (fraction is 8 bit only)
Recommended / default: 2 (16 bit fraction, sub-ms resolution)

The recommended value 2 is a good choice also in the light of most NTP servers. Observations of Windows 2008 R2 as NTP server showed the least 16 fraction bits being always the same (rubbish ?) value no matter the circumstances.
NTP_FRACT_DROP 2 reduces ntpTimestampSum, ntpTimestampDif and ntpTimestampHalf() from 64 bit to 48 bit arithmetic (saving ten / six cycles). NTP_FRACT_DROP 2 also fits well to ntpTimestampToMillies() and setNTPstampAct().

#define ntpAsksPrio ( )

NTP asks for priority.

This expression is true if the NTP client sent a request and waits for the response. Postponing the reaction to this response by using needed resources (NIC, SPI) or having long running thread steps (by SMC block operations e.g.) will detriment the synchronisation accuracy.

As NTP synchronisations are quite seldom (about every minute in good sync state) and turn-around for (recommended) local NTP servers is quite fast it should be feasible for all others to respect this request for priority.


Function Documentation

void ntpTimestampSum ( struct ntpTimestamp_t result,
struct ntpTimestamp_t a,
struct ntpTimestamp_t b 
)

Sum of two NTP time stamps.

This function calculates *result = *a + *b;
No pointer must be null, but two or three of them might point to the same ntpTimestamp_t structure.

ntpTimestampSum, ntpTimestampDif and ntpTimestampHalf are optimised implementations for 64 bit wrong endian (i.e. network byte order).
The former two take 74 cycles and the latter 43. This values are for NTP_FRACT_DROP 0, the recommended value 2 makes all a bit faster.

Parameters:
apoints to first operand (not NULL)
bpoints to second operand (not NULL)
resultpoints to result structure (not NULL)
See also:
NTP_FRACT_DROP
void ntpTimestampDif ( struct ntpTimestamp_t result,
struct ntpTimestamp_t a,
struct ntpTimestamp_t b 
)

Difference of two NTP time stamps.

This function calculates *result = *a - *b;
No pointer must be null, but two of them might point to the same ntpTimestamp_t structure.

ntpTimestampSum, ntpTimestampDif and ntpTimestampHalf are optimised implementations for 64 bit big endian.

Parameters:
apoints to first operand (not NULL)
bpoints to second operand (not NULL)
resultpoints to result structure (not NULL)
See also:
NTP_FRACT_DROP
void ntpTimestampHalf ( struct ntpTimestamp_t result)

Half a NTP time stamp value.

This function calculates *result = *result / 2

The division by 2 is performed for a signed (two complement) 64 bit number. This also gives correct results for large differences in modulo 2**64 bit arithmetic, but will of course fail on very large unsigned numbers >= 2**63.

ntpTimestampSum, ntpTimestampDif and ntpTimestampHalf are optimised implementations for 64 bit big endian.

Parameters:
resultpoints to operand and result structure (not NULL)
See also:
NTP_FRACT_DROP
void ntpTimestampToMillies ( uint16_t *  ms,
struct ntpTimestamp_t ntpStamp 
)

Milliseconds from a NTP time stamp's fraction.

This function converts a NTP time stamp's fraction value (32 bit) into milliseconds in the range 0 .. 999 (16 bit).
The fraction is expected in (wrong) network endianess while the result will be set in the correct byte order.

The implementation is (inline) ASM optimized. It sacrifices a tiny bit of exactness in favor of a slow RISC processor with only byte operations. The effect is the 0 .. 999ms being mapped to 0..992ms.
The assumption behind is the worst case 1% of a second error a being far within the NTP synchronisation abilities of an ATmega-uIP-ENC trio.
Round trip times of 50ms .. 80ms with a Windows 2008 R2 time server in a local network with some switches in between suggest the un-symmetry under these near ideal conditions being at least some milliseconds. Hence the above assumption seems quite adequate.

Parameters:
ntpStampstructure pointed to has fraction in network byte order; never null!
Using a pointer for this 16 bit result instead of a return value is just a work around a GCC inline ASM bug.
mspoints to the result variable where to put
value.fraction converted to ms (0 .. 999) to; never null
See also:
ntpTimestampSum
ntpTimestampHalf
setNTPstampAct
void setNTPstampAct ( struct ntpTimestamp_t ntpTimestamp)

Set a NTP time stamp to actual time.

This function sets the .seconds and .fraction of ntpTimestamp to the actual UTC system time (secTime308UTC(), msAbsClockCount).

On .fraction all but the upper 10 bits are set to zero.

The ms to fraction algorithm implementation is (ASM) optimised for the 8bit AVR RISC. It sacrifices a tiny bit of exactness in favor of great CPU usage savings. The effect is the 0 .. 999ms of msAbsClockCount being mapped to effectively 0..992ms in the NTP stamp fraction.

Parameters:
ntpTimestamppoints to the stamp to be set (never null)
See also:
ntpTimestampToMillies
void adjustNTPtime308UTC ( struct ntpTimestamp_t ntpTimeDif)

Adjust the actual (system) time by a NTP time stamp difference.

This function adjusts the internal date and time (secTime308UTC()) by an amount given as a a NTP time stamp difference to theactual time in s, ms.

Big (>=2**31 in the seconds part) differences will quite naturally act as negative in the sense of second based timing's (and NTP's) modulo 2**32 arithmetic.

This function should be used for any absolute date time adjustments caused by NTP client functions (or may be also other time sources).

The action taken and the flags set in ntpState .lastAdjust depend on the (absolute) difference amount

  • below 8 ms : no action (considered as jitter)
  • -100 .. +50ms : no setting;
        only oscillator speed up / slow down
  • -999 .. + 999ms : setting of time and
        oscillator speed up / slow down
  • above -1 .. +1s : setting of time only
Parameters:
ntpTimeDifthe adjustment difference in seconds,fraction; big values act as negative in the sense of seconds modulo 2**32 arithmetic
See also:
secTime308Loc(void)
secTime308UTC(void)
void ntpInit ( uint8_t  pref2)

Initialise the NTP (client)

This function initialises the NTP structures and (protothread) state machine. The protocol is not started by this function; the work is done by later calls of ntpAppcall(). There is no need to call ntpReset() before.

If "be NTP client" is not set in curIpConf nothing will be done except setting the state to NTP_STATE_RESET.

If the parameter pref2 is true (not 0) the second NTP server in the current IP configuration will be used, if set there. Successful bind to a set NTP server will set the (NTP client) state to NTP_STATE_CONN1 respectively NTP_STATE_CONN2.

Parameters:
pref2if true use second NTP server (if available)
void ntpReset ( void  )

Reset the NTP (client) to initial state.

This function initialises the NTP structures and (protothread) state machine. This function resets all NTPP state to initial. In contrast to ntpInit no trial to connect or bind to NTP ports or servers. The protocol is not (not even indirectly) started.

See also:
ntpInit()
ptfnct_t ntpAppcall ( void  )

Handle NTP (server) events.

This function handles NTP events. And it is a (as a protothread) the NTP client state machine. It has to be called in the udp_appcall in a manner like

   const uint16_t remPort = convert16endian(uip_udp_conn->rport);

   if(remPort == NTP_PORT) {  // NTP protocol
      ntpAppcall();
      return;
   } // NTP protocol