Android Code Snippet DIY NTP Network Atomic Clock Time

I just ported some NTP stuff from an old program.

Querying an NTP server is surprisingly easy: just send it a 48 byte UDP packet with the first byte = 27 and the rest of them 0.

The NTP server should return your 48 byte packet updated with the last 16 bytes set to two 64-bit timestamps, being when it (i) received your request and (ii) soon-after sent its response.

My code "returns" 4 timestamps NTPTimeStamp(1) thru NTPTimeStamp(4), matching T1..T4 of this command-line utility:

https://kb.meinbergglobal.com/kb/public_utilitiy_programs/ntptest_-_check_ntp_server

except that my timestamps are in Java ticks (ms since 1970) for easy use with DateTime formatting etc.

I did this example with B4J but the same principles work in B4A (as in: I've run similar code successfully there too).

B4X:
#Region Project Attributes
    #MainFormWidth: 300
    #MainFormHeight: 300
#End Region

Sub Process_Globals
    Private fx As JFX
    Private MainForm As Form
    Private xui As XUI
    Private Button1 As B4XView

    Dim UDPSocket1 As UDPSocket
   
    Dim NTPPort As Int = 123
    Dim NTP1970 As Long = ((1970 - 1900) * 365 + 17).As(Long) * 86400
   
    Dim NTPTimeStamp(5) As Double  'to match T1..T4 of Meinberg NTPTEST utility
End Sub

Sub AppStart (Form1 As Form, Args() As String)
    MainForm = Form1
    MainForm.RootPane.LoadLayout("Layout1")
    MainForm.Show
   
    UDPSocket1.Initialize("UDP", NTPPort, 8000)
End Sub

Sub SendNtpRequest(NTPServerName As String)
    Dim data(48) As Byte
    data(0) = 27

    Dim Packet As UDPPacket
    Packet.Initialize(data, NTPServerName, NTPPort)
   
    NTPTimeStamp(4) = 0    'so can tell if received reply
    NTPTimeStamp(1) = DateTime.Now
    UDPSocket1.Send(Packet)
End Sub

Sub UDP_PacketArrived (Packet As UDPPacket)
    If Packet.Length < 48 Then Return
   
    NTPTimeStamp(4) = DateTime.Now

    Dim StartFrom As Int = Packet.Offset + 32
    For ServerTimeStamp = 2 To 3
        Dim Temp As Long = 0
        For I = StartFrom To StartFrom + 6    '7 bytes (32 bits seconds + 24 bits fraction)
            Temp = Bit.ShiftLeftLong(Temp, 8) + Bit.And(Packet.Data(I), 0xFF)
        Next
       
        Dim NTPSeconds As Double = Temp / 0x1000000    'shift binary point 24 bits left
        Dim JavaTicks As Double = (NTPSeconds - NTP1970) * 1000

        NTPTimeStamp(ServerTimeStamp) = JavaTicks
       
        StartFrom = StartFrom + 8    'next 64-bit timestamp
    Next
End Sub

Sub Button1_Click
    Dim NTPServerName As String = "au.pool.ntp.org"
   
    SendNtpRequest(NTPServerName)
    Sleep(2000)

    If NTPTimeStamp(4) = 0 Then
        Log("No reply from " & NTPServerName)
        Return
    End If
       
    Log(NTPServerName)
    For I = 1 To 4
        Log("T" & I & " = " & NumberFormat2(NTPTimeStamp(I), 1, 3, 3, False))
    Next
   
    Dim LocalTimeMiddle As Double = (NTPTimeStamp(1) + NTPTimeStamp(4)) / 2
    Dim ServerTimeMiddle As Double= (NTPTimeStamp(2) + NTPTimeStamp(3)) / 2
    Dim DeviceToNTPOffset As Double = ServerTimeMiddle - LocalTimeMiddle
   
    If DeviceToNTPOffset < 0 Then
        Dim Direction As String = "ahead of"
    Else
        Dim Direction As String = "behind"
    End If
   
    Log( _
        "Device clock is " & _
        NumberFormat2(Abs(DeviceToNTPOffset), 1, 1, 1, False) & _
        " ms " & Direction & " NTP server time (roundtrip " & _
        NumberFormat2(NTPTimeStamp(4) - NTPTimeStamp(1), 1, 1, 1, False) & _
        " ms)" _
    )

    DateTime.TimeFormat = "hh:mm:ss.SSS"
    Dim N As Long = DateTime.Now
    Log("Device clock = " & DateTime.Time(N))
    Log("NTP clock = " & DateTime.Time(N + DeviceToNTPOffset))
End Sub
 
Last edited:

emexes

Expert
Licensed User
Sample run. Don't panic about the long roundtrip times: I am at a location with internet supplied wirelessly via Vodafone. When I get back home tomorrow, I'll update the sample using tic.ntp.telstra.net which usually pings around 5 ms.

Log output:
Waiting for debugger to connect...
Program started.
au.pool.ntp.org
T1 = 1708868547753.000
T2 = 1708868548012.398
T3 = 1708868548012.410
T4 = 1708868548126.000
Device clock is 72.9 ms behind NTP server time (roundtrip 373.0 ms)
Device clock = 12:42:29.854
NTP clock = 12:42:29.926
au.pool.ntp.org
T1 = 1708868553886.000
T2 = 1708868554102.279
T3 = 1708868554102.287
T4 = 1708868554216.000
Device clock is 51.3 ms behind NTP server time (roundtrip 330.0 ms)
Device clock = 12:42:35.894
NTP clock = 12:42:35.945
 

emexes

Expert
Licensed User
tic.ntp.telstra.net which usually pings around 5 ms.

4.8 ms ?

ntptest tic.ntp.telstra.net:
Precise system time is supported and used
Host tic.ntp.telstra.net
Request packet: mode 3 (client)
  version 4, leap 3, stratum 0, poll 6, prec -18 (3.81 us)
  root delay:      00000000 (0.000000 s)
  root dispersion: 00000000 (0.000000 s/s)
  reference id:    00000000 ("....")
  Ref time:       00000000.00000000  2036-02-07 06:28:16.000000000
  Org time (T1):  00000000.00000000  2036-02-07 06:28:16.000000000
  Rcv time (T2):  00000000.00000000  2036-02-07 06:28:16.000000000
  Xmt time (T3):  E9818916.238F6DC6  2024-02-22 08:49:26.138907299
  Curr time (T4): E9818916.238F6DC6  2024-02-22 08:49:26.138907299

Response packet: mode 4 (server)
  version 4, leap 0, stratum 2, poll 6, prec -24 (59.60 ns)
  root delay:      000000AF (0.002670 s)
  root dispersion: 0000054B (0.020676 s/s)
  reference id:    27B48E6E (110.142.180.39)
  Ref time:       E9818835.534C027A  2024-02-22 08:45:41.325378565
  Org time (T1):  E9818916.238F6DC6  2024-02-22 08:49:26.138907299
  Rcv time (T2):  E9818916.2D1D6CF4  2024-02-22 08:49:26.176230248
  Xmt time (T3):  E9818916.2D1E13C9  2024-02-22 08:49:26.176240192
  Curr time (T4): E9818916.24CA0572  2024-02-22 08:49:26.143707599

turnaround:        4800.300 us  (T4 - T1)
server latency:       9.944 us  (T3 - T2)
computed delay:    4790.356 us  ((T4 - T1) - (T3 - T2))
computed offset:  34927.771 us  (((T2 - T1) + (T3 - T4)) / 2)
 

emexes

Expert
Licensed User
1708872137550.png
 
Top