As of PowerNet v4.30 (8 May 2008) the user interface to the SNTP data has changed and is not compatible with the previous one.
The code in <PNet>\SNTP implements most of an SNTP client according to RFC1361 and RFC4330. See also RFC1305. Do not attempt to modify this code until you have absorbed these RFCs.
If you do not have an NTP server on your network, there are "public" ones available. See www.ntp.org for details. Note when testing that advertised public servers are not always available. Your ISP probably maintains a functioning NTP server.
0 equ SNTPdebug? \ -- x
Set this non-zero to display SNTP debug information during
execution.
1 equ SNTPactions? \ -- x
Defined the actions performed by default for timestamps.
0 Dummy action
1 Set clock
... user defined action
#123 equ SNTPport# \ -- port#
The standard SNTP port number. The client (us) and the
server use the same port number.
STRUCT /sntp \ -- len
SNTP payload structure after the IP and UDP headers.
The Timestamp fields contain a 32.32-bit fractional
time in network (big-endian) format. The first cell contains
seconds, measured since 1st Jan 1900, as per RFC1361. The
second cell contains a fractional second.
If you are using the SNTP code with the Unix calendar code
in Examples/UnixTime.fth\, NTP timestamps are
based from 1 Jan 1900, which has an LSECONDS
value of
1291876096+86400=1291962496, so add 1291962496 to the NTP
seconds value to produce a Unix time value.
If you are using this code with NTP timestamps, these are
based from 1 Jan 1900, which has an LSECONDS
value of
1291876096+86400=1291962496, so add 1291962496 to the NTP
seconds value to produce a Unix time value. Note that the
Unix LSECONDS
counter rolls over in 2036. Suitable
code to convert LSECONDS
to time and date can be found
in your cross compiler Examples/UnixTime.fth.
If you are planning a product which needs to cope with dates beyond 2035, note the following from RFC4330.
As the NTP timestamp format has been in use for over 20 years, it is possible that it will be in use 32 years from now, when the seconds field overflows. As it is probably inappropriate to archive NTP timestamps before bit 0 was set in 1968, a convenient way to extend the useful life of NTP timestamps is the following convention: If bit 0 is set, the UTC time is in the range 1968- 2036, and UTC time is reckoned from 0h 0m 0s UTC on 1 January 1900. If bit 0 is not set, the time is in the range 2036-2104 and UTC time is reckoned from 6h 28m 16s UTC on 7 February 2036. Note that when calculating the correspondence, 2000 is a leap year, and leap seconds are not included in the reckoning.
struct /SNTPparams \ -- len
A set of parameters to configure the SNTP service
This structure is the simplest way to interact with the SNTP
module. The elements have the following meaings :-
|
- the IPv4 address of the SNTP server to use. |
|
- log2 of the interval between polls in seconds, e.g. 5 for a 32 second interval. This value is set to 5 by default, and you should probably increase it when you are satisfied with your SNTP handling. |
*\fo{sp.xtSetTime |
- the xt of a callback to yourapplication to process the received SNTP packet. This iscalled whenever an SNTP packet is received.The word receives the address and length of the UDP data,i.e. a |
|
- an optional configuration, and isnot required for pure SNTP. If set, it must put the currenttimestamp on the stack as a 32.32-bit fractional RFC1361 time.The stack effect is |
int sp.SNTPserver \ IP address of the server to use. Must be first. int sp.PollInterval \ Time between SNTP polls, in seconds, as a power of two int sp.xtSetTime \ Callback to alert caller to a new NTP packet int sp.xtGetTime \ xt of word to provide current timestamp, or zero if n/a end-struct
idata create SNTPparams \ -- addr
The /SNTPparams
parameter block in IDATA space (RAM).
Note: This layout must match the layout of the
/SNTPparams
structure above. Patch this table with
the actions you supply.
SNTPparams equ SNTPserver
Address holding SNTP server IP address.
The SNTP system is controlled by a state machine, which is
run whenever the timer *\fo{SNTPtimer) times out. The action
performed is responsible for setting the next state and updating
the timer. The state machine actions are performed by
SNTPidle ( -- )
which is called in the service task.
States of the SNTP state machine. See RFC1361
------- ------------
| | | |
| INIT- |----Parameter set from caller--->| CONFIGURED |<-------+
| REBOOT| +---| | |
| | | ------------ |
------- | | |
| NTPREQUEST Sent |
NTP Packet Received | |
| +---------------+
-------- ^
| | |
| ACTIVE |--- Timeout----------+
| |
--------
[sm smState smSNinit \ initialisation, wait for params from caller smState smSNconfigured \ Send SNTP Request, set up timeout smState smSNactive \ SNTP answer received within timeout period sm] #smSNactions \ number of states in this machine
variable SNTPTimer \ -- addr
Holds the TICKS
time at which the next SNTP action will
be performed, 0 for no action.
variable smSNcount \ -- addr
Down counter used to provide repetitions and timeouts.
variable smSNstate \ -- n
Holds current SNTP state.
create smSNactions \ -- addr
Execution table containing the xts of the action words.
This in CDATA
space and is filled in later.
: setSNstate \ xt state --
INTERPRETER
word that sets the action of the given state.
: SNTPidle \ --
Performed periodically to do SNTP actions.
: SetSNTPtimer \ ms --
Set the SNTP timer to time out ms later.
: .SNstate \ --
Debug tool to display the current state of the SNTP state machine.
: toSNTPstate \ ms n state --
The given state will be executed after the given
period in ms and n times if sensitive to the number of
times. Note that this specifies the interval until the
state is executed, not the repetition rate of the state.
variable SNTPreply? \ -- addr
Set non-zero when an SNTP reply has been received, but the
data has not yet been been processed. During this time any
following SNTP packets will be discarded.
: (SNTPPSend) { *pb ipaddr -- }
Send SNTP data as a UDP packet. The parameters are *pb,
a pointer to a PBuff containing the UDP data, and ipaddr,
the IP address to send to.
: SendSNTP { | *pb *sntp -- }
Send an SNTP message.
Each state is controlled by three values defined here.
smSNcount
reaches zero.
: goSNTPInit \ --
Go to the smSNnit
state - stop SNTP activity and wait
for parameters from the calling app.
: goSNTPConfigured \ --
Go to the smSNconfigured
state; start the SNTP process.
: goSNTPActive \ --
Go to the smSNactive
state - SNTP data has been received
: doSNinit \ --
SNTPauto?
=nz: Do nothing until SNTP server has been
set, either manually or by DHCP, and then start SNTP.
: doSNconfigured \ --
"Configured" state has timed out - send SNTP request and
wait for response or timeout
: doSNactive \ --
"active" state has timed out; revert to "configured"
: runSNTP \ --
Start the SNTP process and wait until a valid time has been
returned.
: RxSNTP \ *pb --
Process received SNTP packets.
When we get here we know this is from port 123
and that the Ethernet packet was either addressed to us or
was broadcast.
create SNTPServiceStruct \ -- addr
Holds the UDP Service info to run the SNTP code on receipt
of SNTP packets, or idle on timeout. Not that this must
match the /UDPService
structure in UDP.fth.
' doSNinit smSNinit setSNstate ' doSNconfigured smSNconfigured setSNstate ' doSNactive smSNactive setSNstate