Jump to content

Update system time based on NTP server time


CYCho
 Share

Recommended Posts

Based on codes in an old AutoIt Forum, I made the following code and it works perfectly. But I have couple of questions on how it works. I have to ask the original writer of this code(@Fzz), but he doesn't seem to be active now. Can someone please let me know the internal workings of this code? I'm especially curious about the meaning of packet sent to the NTP server.  I don't know what the 96-byte long hex string with a lot of 0's means. I don't know why the hex string is converted to a decimal character string instead of using the decimal characters to start with. I don't know the benefit of using Call() function over using funtion name followed by ().

;~ Many thanks to @TheXman for his kind guidance.
;~ https://www.autoitscript.com/forum/topic/200643-pulling-time-from-ntp-server/?do=findComment&comment=1439629

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_AU3Check_Parameters=-w 3 -w 4 -w 5 -w 6 -d
#AutoIt3Wrapper_Outfile=NTP_Time_V4.exe
#AutoIt3Wrapper_Res_Fileversion=4.0
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#RequireAdmin
#include <Constants.au3>
#include <Date.au3>

Global $hTimeoutTimer = TimerInit()
While 1
    If Ping("www.google.com") > 0 Then ExitLoop
    If TimerDiff($hTimeoutTimer) > 120000 Then
        WriteLog(0, "System has no internet connection")
        Exit
    EndIf
    Sleep(50)
WEnd

Global $NTP_Server = 'time.google.com', $NTP_Time

NTP_GetTime()

System_SetTime()


Func System_SetTime()
    ; 2019/10/28 23:09:52.522
    ; 12345678901234567890123
    Local $m = StringMid($NTP_Time, 6, 2)
    Local $d = StringMid($NTP_Time, 9, 2)
    Local $y = StringMid($NTP_Time, 1, 4)
    Local $h = StringMid($NTP_Time, 12, 2)
    Local $mi = StringMid($NTP_Time, 15, 2)
    Local $s = StringMid($NTP_Time, 18, 2)
    Local $ms = StringMid($NTP_Time, 21, 3)
    ;~ Sets the new current time to the computer
    Local $tCurr = _Date_Time_EncodeSystemTime($m, $d, $y, $h, $mi, $s, $ms)
    Local $tTime = _Date_Time_GetSystemTime()
    _Date_Time_SetSystemTime(DllStructGetPtr($tCurr))

    Local $aTime = _Date_Time_SystemTimeToArray($tTime)
    WriteLog($aTime, "Update Time: " & $NTP_Time & " UTC")
EndFunc ;==>System_SetTime

Func NTP_GetTime()
    Local $tBuffer
    Local $aSocket
    Local $xRequest = Binary(""), $xResponse = Binary(""), $hTimeoutTimer, $hEchoTimer, $iDelay
    Local $iSeconds= 0, $iFractions = 0, $iMsecs = 0

    ; Create the NTP request using a 48 byte buffer
    $tBuffer = DllStructCreate("byte[48]")
    DllStructSetData($tBuffer, 1, 0x23) ; 00 100 011 = LI(0) / VN(4-NTPv4) / Mode(3-Client)

    ; Copy the buffer to a variable for sending
    $xRequest = DllStructGetData($tBuffer, 1)

    ; Send NTP request
    UDPStartup()
    If @error Then
        WriteLog(0, "UDPStartup failed - @error = " & @error)
        Exit
    EndIf
    OnAutoItExitRegister("udp_shutdown")

    $aSocket = UDPOpen(TCPNameToIP($NTP_Server), 123)
    If @error Then
        WriteLog(0, "UDPOpen failed - @error = " & @error)
        Exit
    EndIf
    Sleep(50)

    $hTimeoutTimer = TimerInit()
    While 1
        $hEchoTimer = TimerInit()
        UDPSend($aSocket, $xRequest)
        If @error Then
            WriteLog(0, "UDPSend failed - @error = " & @error)
            Exit
        EndIf

        Do
            $xResponse = UDPRecv($aSocket, DllStructGetSize($tBuffer), $UDP_DATA_BINARY)
            If @error Then
                WriteLog(0, "UDPRecv failed - @error = " & @error)
                Exit
            EndIf
        Until $xResponse <> Binary("")
        $iDelay = Round(TimerDiff($hEchoTimer)/2)
        If $iDelay < 100 Then
            ExitLoop
        EndIf
        If TimerDiff($hTimeoutTimer) > 120000 Then ; If no response within 2 seconds, then exit
            WriteLog(0, "UDPRecv timed out")
            Exit
        EndIf
        Sleep(100)
    WEnd

    ; Close the socket
    UDPCloseSocket($aSocket)

    ; Parse timestamp values
    ; - Current time is calculated from the xmit timestamp in NTP response header
    ; - Xmit timestamp seconds is a big-endian, uint32, at binary position 41
    ; - Xmit timestamp fraction is a big-endian, uint32, at binary position 45
    $iSeconds = Dec(Hex(BinaryMid($xResponse, 41, 4)), $NUMBER_64BIT) ; seconds since 1900-01-01 00:00:00
    $iFractions = Dec(Hex(BinaryMid($xResponse, 45, 4)), $NUMBER_64BIT) ; the maximum value is 0xFFFFFFFF, which represents 1 second
    $iMsecs = Round($iFractions / 2^32 * 1000) + $iDelay ; It normally takes about 50 milsecs to get NTP time
    If $iMsecs >= 1000 Then
        $iSeconds += 1
        $iMsecs -= 1000
    EndIf

    ; Current NTP time
    $NTP_Time = _DateAdd("s", $iSeconds, "1900/01/01 00:00:00") & StringFormat(".%03i", $iMsecs)
EndFunc   ;==>NTP_GetTime

Func udp_shutdown()
    UDPShutdown()
EndFunc   ;==>udp_shutdown

Func WriteLog($pTime, $sMessage)
    Local $fn, $sTime
    If $pTime = 0 Then
        $fn = FileOpen(@ScriptDir & "\TimeSync Failed.log", 2)
        $sTime = _NowCalc()
    Else
        $fn = FileOpen(@ScriptDir & "\TimeSync.log", 2)
        FileDelete(@ScriptDir & "\TimeSync Failed.log")
        $sTime = StringFormat("System Time: %04d/%02d/%02d %02d:%02d:%02d.%03d UTC", $pTime[2], $pTime[0], $pTime[1], $pTime[3], $pTime[4], $pTime[5], $pTime[6])
    EndIf
    FileWrite($fn, $sTime & @CRLF & $sMessage)
    FileClose($fn)
EndFunc   ;==>WriteLog

 

Edit: The above code is my latest version as of 2019/11/24, reflecting all the suggestions thankfully offered by @TheXman.

Edit: The compiled version of this code(NTP_Time_V4.exe) was cleared by Microsoft Windows Defender from false detection as of Definition 1.305.2722.0. - 2019/11/24

 

 

NTP_Time_V4.exe.zip

Edited by CYCho
Link to comment
Share on other sites

11 minutes ago, Earthshine said:

Read the help file to start with. 

Thank you for your prompt response. I thought I read the help file for most of the key words used here, but I could not find answers to my questions. Could you direct me to what key words I should read in help file?

Edited by CYCho
Link to comment
Share on other sites

Here a modern way to get date/time from all over the world :

#include <Constants.au3>

Local $String = BinaryToString(InetRead ("http://worldtimeapi.org/api/timezone/America/Toronto.txt",1))
MsgBox (0,"",$String)
;Local $String = BinaryToString(InetRead ( "http://worldtimeapi.org/api/timezone/Europe/Amsterdam",1))
Local $Time = StringRegExp ($String,'datetime: (.+?)T(\d+:\d+:\d+)', $STR_REGEXPARRAYMATCH)
MsgBox ($MB_SYSTEMMODAL,"",$Time[0] & " " & $Time[1])

 

Link to comment
Share on other sites

@Nine, Thanks. your code really simplifies the process of updating the system time.

Edit:

Quote

The API can go down from time-to-time, for relatively long periods.

This is what World Time API web site says. If so, I would still like to get time from other reliable NTP servers.

Edited by CYCho
Link to comment
Share on other sites

3 hours ago, CYCho said:

Can someone please let me know the internal workings of this code? I'm especially curious about the meaning of packet sent to the NTP server.

The internal workings of the code are directly related to the internal workings of the NTP protocol.  Everything you want to know about the NTP protocol can be found in RFC5905.  The RFC for NTP v4 can be found HERE.  It also includes references to previous version RFCs and updates to the RFC5905.

4 hours ago, CYCho said:

I don't know why the hex string is converted to a decimal character string instead of using the decimal characters to start with. I don't know the benefit of using Call() function over using funtion name followed by (). 

As with most programming languages, there are many ways to achieve the same result.  The reason that the author chose to do the binary conversions the way  he or she did is because that is the way he or she chose to do it.

Link to comment
Share on other sites

I replaced:

MakePacket("1b0e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")

with:

"300000000000000000000000000000000000000000000000"

and the result was as good. The revised string is 3, indicating client mode, followed by 47 zeroes. This way I could do away with converting a binary string to a character string.

The revised code:

;~ This code is a tweak of examples in AutoIt Forum (https://www.autoitscript.com/forum/topic/43079-ntp-c-to-autoit-conversvion/)

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7
#AutoIt3Wrapper_Outfile=NTP_Time_V2.exe
#AutoIt3Wrapper_Res_Fileversion=2.0
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#RequireAdmin
#include <Date.au3>

Global $NTP_Server[3] = ['time.google.com','time.windows.com', 'time.nist.gov'], $NTP_IP[3], $ntp, $fn

Local $time = TimerInit()
While $NTP_IP[0] & $NTP_IP[1] & $NTP_IP[2] = ""
    Call('check_internet_connectivity')
    If TimerDiff($time) > 120000 Then
        $fn = FileOpen(@ScriptDir & '\timesync-failed.txt', 2)
        FileWrite($fn, "Time sync failed due to no connection.")
        FileClose($fn)
        Exit
    EndIf
    Sleep(100)
WEnd

For $i = 0 To 2
    If  $NTP_IP[$i] <> '' Then
        Local $adata = Call('NTP_Connect', $NTP_Server[$i])
        If $adata <> '' Then
            Call('Set_Time', $adata)
            $ntp = $NTP_Server[$i]
            ExitLoop
        EndIf
    EndIf
Next

If $ntp <> '' Then
    $fn = FileOpen(@ScriptDir & '\timesync.txt', 2)
    FileWrite($fn, "Time was sync'ed at " & @YEAR & "." & @MON & "." & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " based on " & $ntp)
    FileClose($fn)
    FileDelete(@ScriptDir & '\timesync-failed.txt')
Else
    $fn = FileOpen(@ScriptDir & '\timesync-failed.txt', 2)
    FileWrite($fn, "Time sync failed due to no data from ntp servers.")
    FileClose($fn)
EndIf


Func check_internet_connectivity() ; check availability of time servers
    TCPStartup()
    For $i = 0 to 2
        $NTP_IP[$i] = TCPNameToIP ( $NTP_Server[$i])
        Sleep(250)
    Next
    TCPShutdown ()
EndFunc

Func NTP_Connect($NTP_Server) ; read time from ntp server as unsigned hex
    UDPStartup()
    Local $socket = UDPOpen(TCPNameToIP($NTP_Server), 123)
    UDPSend($socket, "300000000000000000000000000000000000000000000000")
    Local $data, $time = TimerInit()
    While $data = ""
        $data = UDPRecv($socket, 100) ; normally takes about 60 milsecs
        If TimerDiff($time) > 10000 Then ExitLoop
    WEnd
    UDPCloseSocket($socket)
    UDPShutdown()
    Return $data
EndFunc

Func Set_Time($bdata) ; decrypt the time from returned hex string and apply new time to system
;   $bdata = "0x340130EC0000000000000014474F4F47E15D224F505D83853030303030303030E15D224F505D8386E15D224F505D8388"
    Local $unsignedHexValue = StringMid($bdata, 83, 8); Extract time from packet. Disregards the fractional second.
    Local $value = UnsignedHexToDec($unsignedHexValue)
    Local $UTC = _DateAdd("s", $value, "1900/01/01 00:00:00")
    ;~ Extracts the data & time into vars
    ;~ Date format & offsets
    ;~ 2009/12/31 19:26:05
    ;~ 1234567890123456789  [1 is start of string]
    Local $m = StringMid($UTC, 6, 2)
    Local $d = StringMid($UTC, 9, 2)
    Local $y = StringMid($UTC, 1, 4)
    Local $h = StringMid($UTC, 12, 2)
    Local $mi = StringMid($UTC, 15, 2)
    Local $s = StringMid($UTC, 18, 2)
    ;~ Sets the new current time to the computer
    Local $tCurr = _Date_Time_EncodeSystemTime($m, $d, $y, $h, $mi, $s)
    _Date_Time_SetSystemTime(DllStructGetPtr($tCurr))
EndFunc

Func UnsignedHexToDec($n) ; decript UnsignedHexToDec
    Local $ones = StringRight($n, 1)
    $n = StringTrimRight($n, 1)
    Return Dec($n) * 16 + Dec($ones)
EndFunc

 

Edited by CYCho
Link to comment
Share on other sites

On 10/24/2019 at 10:36 PM, CYCho said:

It once again proves how little I know and I have to learn a lot. Maybe I should just use the code as it is.

I'm not saying that the original code couldn't have been optimized and cleaned up a little, but it worked and the binary request was correct.  But your change makes absolutely no sense.  The first word of advice is if you don't know what a particular piece of code is doing, DON'T CHANGE IT!  The fact that you got back a good response with your "modification" was sheer luck because, lucky for you in this particular case, most of the information in the NTP request header is ignored.  You could have thrown almost any data in the request packet and gotten back a response with a timestamp in it.

Obviously you do not work with binary data very often.  What the MakePacket() function did was change the hex string representation of a binary packet into an actual binary packet.  I'm still trying to understand how you came up with your "modification" though.  There is a big difference between 0x1B and "3" and 0x00 and "0".  They DO NOT equate to the same binary value.  And binary packets are what is sent and received in the NTP protocol, not strings.  For example, 0x1B in binary is 00011011.  "3", in binary, assuming it is using ascii or UTF-8 encoding, is 00110011 (0x33).  0x00 in binary is 00000000.  "0" in binary, assuming ascii or utf-8 encoding, is 00110000 (0x30).

If you wanted to get rid of the MakePacket() function, then you could have done something like:

;Replace
MakePacket("1b0e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")

;With
Binary("0x1b0e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000")

If you look at the RFC, the first 8 bits of the request/response header is made up of 3 values, LI, VN, and Mode.  LI, the Leap Indicator, is the first 2 bits.  VN, the NTP version number, is the next 3 bits.  And the last 3 bits designates the mode, which in you case should be Client (3).  That is why the first byte of request, in the original code, was 0x1B.  That breaks down like so:

0x1B = 00011011
00 = Leap Indicator (0)
011 = Version Number (NTPv3)
011 = Mode (3-Client)

With your "modification, it breaks down like so:

0x33 = 00110011
00 = Leap Indicator (0)
110 = Version Number (NTPv6) ???
011 = Mode (3-Client)

So the fact is that you got lucky in that you got the Mode correct (Client Request).  I guess the NTP server just ignored the fact that you said that you were using a non-existent NTP version or sent back an error packet with a valid timestamp. 😃

 

Edited by TheXman
corrected a typo pointed out by CYCho
Link to comment
Share on other sites

@TheXman, Thank you for your detailed explanation. I found that all of the following 3 options worked well.

1. UDPSend($socket, Binary("0x1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))
2. UDPSend($socket, Binary("1b0000000000000000000000000000000000000000000000"))
3. UDPSend($socket, "1b0000000000000000000000000000000000000000000000")

It seems to me that the NTP server automatically converts an incoming character string to binary format. If so, can I just use the simplest one (option 3) without Binary()?

Edited by CYCho
Link to comment
Share on other sites

The NTP server doesn't do any conversions of the inbound data from string representations of binary to binary.  The NTP server reads all data in the header as binary, regardless of how you send it.  Option 1 is correct.  Options 2 & 3 are incorrect.  I already explained, in detail, why it happened to work in this specific case.  This is second time that I am telling you the same thing.  That means you didn't understand what I told you the first time.  Binary concepts appear to be beyond your comprehension at the moment.  If you are really interested in understanding my answer, maybe it would benefit you to spend some time understanding the difference between binary data and string representations of binary data.  Also, if you are really interested in the NTP protocol, you should spend more time reading and absorbing the information in the RFC, especially as it pertains to sending client requests, the request header, and the response packet.  As for binary data, I won't even go into the subject big-endian vs little-endian and 1's complement vs 2's complement.  It would probably just confuse you even more.  For the record, the NTP protocol users 2's complement and big-endian for its numbers.

 

Here's a simple script that demonstrated the difference between actual binary data and string representations of binary data.  It also shows the difference between your option 1, 2 & 3.  Actually, options 2 & 3 send the same binary data.

ConsoleWrite(Binary("0x1B0000") & @CRLF)
ConsoleWrite(Binary("1B0000") & @CRLF)
ConsoleWrite("1B0000" & @CRLF)

Output:

0x1B0000
0x314230303030
1B0000

 

Edited by TheXman
Link to comment
Share on other sites

You're quite welcome. 

I love AutoIt and have used it for over 12 years.  :ILA2:  If I were going to choose a language to learn, at an age well over 60, I would have chosen AutoIt too.  Kudos to you!  :thumbsup:

Link to comment
Share on other sites

My final version reflecting @TheXman's correction:

;~ This code is a tweak of examples in AutoIt Forum (https://www.autoitscript.com/forum/topic/43079-ntp-c-to-autoit-conversvion/)

#Region ;**** Directives created by AutoIt3Wrapper_GUI ****
#AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w- 4 -w 5 -w 6 -w- 7
#AutoIt3Wrapper_Outfile=NTP_Time_V2.exe
#AutoIt3Wrapper_Res_Fileversion=2.0
#EndRegion ;**** Directives created by AutoIt3Wrapper_GUI ****

#RequireAdmin
#include <Date.au3>

Local $NTP_Server= 'time.google.com', $NTP_IP, $fn

Local $time = TimerInit()
While $NTP_IP = ""
    TCPStartup()
    $NTP_IP = TCPNameToIP($NTP_Server)
    TCPShutdown ()
    If TimerDiff($time) > 120000 Then
        $fn = FileOpen(@ScriptDir & '\timesync-failed.txt', 2)
        FileWrite($fn, "Time sync failed due to no connection.")
        FileClose($fn)
        Exit
    EndIf
    Sleep(100)
WEnd

Local $adata = Call('NTP_Connect')

If $adata <> '' Then
    Call('Set_Time', $adata)
    $fn = FileOpen(@ScriptDir & '\timesync.txt', 2)
    FileWrite($fn, "Time was sync'ed at " & @YEAR & "." & @MON & "." & @MDAY & " " & @HOUR & ":" & @MIN & ":" & @SEC & " based on Google time server")
    FileClose($fn)
    FileDelete(@ScriptDir & '\timesync-failed.txt')
Else
    $fn = FileOpen(@ScriptDir & '\timesync-failed.txt', 2)
    FileWrite($fn, "Time sync failed due to no data from Google time server.")
    FileClose($fn)
EndIf


Func NTP_Connect() ; read time from ntp server as unsigned hex
    UDPStartup()
    Local $socket = UDPOpen(TCPNameToIP($NTP_Server), 123)
    UDPSend($socket, Binary("0x1b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"))
    Local $data, $time = TimerInit()
    While $data = ""
        $data = UDPRecv($socket, 100) ; normally takes about 60 milsecs
        If TimerDiff($time) > 10000 Then ExitLoop
    WEnd
    UDPCloseSocket($socket)
    UDPShutdown()
    Return $data
EndFunc

Func Set_Time($bdata) ; decrypt the time from returned hex string and apply new time to system
    ;~ $bdata = "0x340130EC0000000000000014474F4F47E15D224F505D83853030303030303030E15D224F505D8386E15D224F505D8388"
    Local $unsignedHexValue = StringMid($bdata, 83, 8); Extract time from packet. Disregards the fractional second.
    Local $value = UnsignedHexToDec($unsignedHexValue)
    Local $UTC = _DateAdd("s", $value, "1900/01/01 00:00:00")
    ;~ Extracts the data & time into vars
    ;~ Date format & offsets
    ;~ 2009/12/31 19:26:05
    ;~ 1234567890123456789  [1 is start of string]
    Local $m = StringMid($UTC, 6, 2)
    Local $d = StringMid($UTC, 9, 2)
    Local $y = StringMid($UTC, 1, 4)
    Local $h = StringMid($UTC, 12, 2)
    Local $mi = StringMid($UTC, 15, 2)
    Local $s = StringMid($UTC, 18, 2)
    ;~ Sets the new current time to the computer
    Local $tCurr = _Date_Time_EncodeSystemTime($m, $d, $y, $h, $mi, $s)
    _Date_Time_SetSystemTime(DllStructGetPtr($tCurr))
EndFunc

Func UnsignedHexToDec($n) ; decript UnsignedHexToDec
    Local $ones = StringRight($n, 1)
    $n = StringTrimRight($n, 1)
    Return Dec($n) * 16 + Dec($ones)
EndFunc

 

Edited by CYCho
Link to comment
Share on other sites

On 10/26/2019 at 12:20 AM, TheXman said:

0x1B = 00001011
00 = Leap Indicator (0)
001 = Version Number (NTPv1)
011 = Mode (3-Client)

This should be read like below:

0x1B = 00011011
00 = Leap Indicator (0)
011 = Version Number (NTPv3)
011 = Mode (3-Client)

Link to comment
Share on other sites

Yes, you are correct.  Nice catch. 😀

Link to comment
Share on other sites

@CYCho

Here's my "quick & dirty" interpretation of the original NTP time request script with a little more error checking, a lot less binary string conversions, and a lot of extra documentation of each step in case you want to study and modify it for your own purposes.

#include <Constants.au3>
#include <date.au3>


ntp_example()

Func ntp_example()
    Const $NTP_SERVER   = "time.windows.com"
    Const $MY_TZ_OFFSET = -5 ; Timezone offset in hours

    Local $tBuffer
    Local $aSocket
    Local $xRequest = Binary(""), $xResponse = Binary("")
    Local $hTimeoutTimer
    Local $iSeconds = 0

    ; Create the NTP request using a 48 byte buffer
    $tBuffer = DllStructCreate("byte[48]")
    DllStructSetData($tBuffer, 1, 0x23) ; 00 100 011 = LI(0) / VN(4-NTPv4) / Mode(3-Client)

    ; Copy the buffer to a variable for sending
    $xRequest = DllStructGetData($tBuffer, 1)
    ConsoleWrite("DEBUG: Binary request  = " & $xRequest & @CRLF)

    ; Send NTP request
    UDPStartup()
    If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPStartup failed - @error = " & @error)
    OnAutoItExitRegister("udp_shutdown") ; Do UDPShutdown upon exit

    $aSocket = UDPOpen(TCPNameToIP($NTP_SERVER), 123)
    If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPOpen failed - @error = " & @error)

    UDPSend($aSocket, $xRequest)
    If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPSend failed - @error = " & @error)

    ; Receive NTP response
    $hTimeoutTimer = TimerInit()
    Do
        If TimerDiff($hTimeoutTimer) > 2000 Then ExitLoop ; If no response within 2 seconds, then exit the loop

        $xResponse = UDPRecv($aSocket, DllStructGetSize($tBuffer), $UDP_DATA_BINARY)
        If @error Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPRecv failed - @error = " & @error)
    Until $xResponse <> Binary("")

    ConsoleWrite("DEBUG: Binary response = " & Binary($xResponse) & @CRLF)

    ; Close the socket
    UDPCloseSocket($aSocket)

    ; If the request timed out, exit with a message
    If $xResponse = Binary("") Then Exit MsgBox($MB_ICONERROR, "ERROR", "UDPRecv timed out")

    ; Get & display timestamp
    ; - UTC timestamp is taken from xmit timestamp in NTP response
    ; - Xmit timestamp is a big-endian, uint32, at binary position 41
    ; - Xmit timestamp represents the number of seconds since 1900-01-01 00:00:00
    $iSeconds = Dec(Hex(BinaryMid($xResponse, 41, 4)), $NUMBER_64BIT)

    ConsoleWrite(StringFormat("DEBUG: Xmit timestamp  = 0x%s (%s)", Hex(BinaryMid($xResponse, 41, 4)), $iSeconds) & @CRLF)

    ConsoleWrite("INFO: UTC date/time   = " & _DateAdd("s", $iSeconds, "1900/01/01 00:00:00") & @CRLF)
    ConsoleWrite("INFO: Local date/time = " & _DateAdd("s", $iSeconds + (3600 * $MY_TZ_OFFSET), "1900/01/01 00:00:00") & @CRLF)
EndFunc

Func udp_shutdown()
    UDPShutdown()
EndFunc

 

Edited by TheXman
Fixed an insignificant typo
Link to comment
Share on other sites

@TheXman Your code looks and works like absolutely professional. I will study it. I am glad to see that you started $xRequest with 0x23 because that was what I was thinking after reading your earlier posts. My next goal is to get and set milliseconds as well. I will try and may come back to you for help if I cannot do it alone. Thank you.

Link to comment
Share on other sites

@CYCho

Thank you for the kind words.

 

3 hours ago, CYCho said:

My next goal is to get and set milliseconds as well. I will try and may come back to you for help if I cannot do it alone.

Calculating milliseconds from the uint32 Fraction field can seem a bit intimidating at first.  Just keep in mind that the Fraction value represents the number of picosecond units.  I won't spoil the fun of figuring it out on your own unless you want me to.  Feel free to ask for help if you need it (which I'm sure you probably won't).  :) 

Edited by TheXman
Link to comment
Share on other sites

@TheXman After several hours of googling, I came up with the following code for calculating fractions of NTP time in millisecs. Your review and comment will be appreciated.

$iMilliSecs = Int(Dec(Hex(BinaryMid($xResponse, 45, 4)), $NUMBER_64BIT)/2^32*1000)
$iMilliSecs = StringFormat("%03i", $iMilliSecs)

 

Edited by CYCho
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
×
  • Create New...