0

At the moment I'm trying to decode a packet from a ntp server. I'm trying it in AutoIT, that's a simple script language.

I found some code but the problem is, I need the milliseconds too. The code only provides seconds.

Thats the code I found:

Local $ntpServer = 'pool.ntp.org'

UDPStartup()

Dim $socket = UDPOpen(TCPNameToIP($ntpServer), 123)
If @error <> 0 Then Return SetError(1)

; $status = UDPSend($socket, MakePacket('1b0e010000000000000000004c4f434ccb1eea7b866665cb00000000000000000000000000000000cb1eea7b866665cb'))
Local $status = UDPSend($socket, MakePacket('1b0e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
If $status = 0 Then Return SetError(1)

Local $data = '', $a = TimerInit() ; Timer setzen, damit im Fehlerfall die Schleife abgebrochen wird

While $data = ''
    $data = UDPRecv($socket, 100)
    Sleep(100)

    If TimerDiff($a) > 1000 Then ExitLoop ; Wenn Timer > 1sek. (Fehler), dann Schleife verlassen
WEnd

If $data <> '' Then
    UDPShutdown()

    Local $unsignedHexValue = StringMid($data, 83, 8) ; Extract time from packet. Disregards the fractional second.
    Local $value            = UnsignedHexToDec($unsignedHexValue)
    Local $TZinfo           = _Date_Time_GetTimeZoneInformation()
    Local $TZoffset         = -(UnsignedToLong($TZinfo[1]) + (UnsignedToLong($TZinfo[4])*($TZinfo[0]<2)) + (UnsignedToLong($TZinfo[7])*($TZinfo[0]=2)))
    Local $UTC              = _DateAdd('s', $value, '1900/01/01 00:00:00')

    Return _DateAdd('n', $TZoffset, $UTC)
EndIf

SetError(1)

It works fine for seconds. As you see, there is a comment "Disregards the fractional second". So, I checked how a ntp packet works and it shows that you have 32bit for seconds, 32 bits for fractional seconds and then another 64 bits with secs and fractions sine 1.1.1900.

In the code he uses 8 chars from the packet (I have no clue why he starts at 83), convert that from hex to dec and then you have the time. So, my first thought to get the milliseconds was, get the next 8 chars, convert that and I have the milli secs, but it's not.

If I get the time four times and let the programm sleep between that, then print out the time with milliseconds I get stuff like that:

2019/02/14 14:29:56.987628606

2019/02/14 14:29:56.243...

2019/02/14 14:29:56.388...

2019/02/14 14:29:57.1107...

That can't be correct. So there is some mistake in decoding the time packet. Anyone know how? I don't need the time extrem exactly like on the millisecond, its enough in a range of 200 - 300 ms.

Edit: Okay, as far as I tested out, the problem is at the converting of the fraction to millisecs.

For all the infos wanted in the comments, thats the complete code:

  Func _TimeSync()
      Local $ntpServer = 'pool.ntp.org'
      UDPStartup()
      Dim $socket = UDPOpen(TCPNameToIP($ntpServer), 123)
      If @error <> 0 Then Return SetError(1)
      ;$status = UDPSend($socket, MakePacket('1b0e010000000000000000004c4f434ccb1eea7b866665cb00000000000000000000000000000000cb1eea7b866665cb'))
      Local $status = UDPSend($socket, MakePacket('1b0e01000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'))
      If $status = 0 Then Return SetError(1)
      Local $data = '', $a = TimerInit() ; Timer setzen, damit im Fehlerfall die Schleife abgebrochen wird
      While $data = ''
          $data = UDPRecv($socket, 100)
          sleep(100)
          If TimerDiff($a) > 1000 Then ExitLoop ; Wenn Timer > 1sek. (Fehler), dann Schleife verlassen
      WEnd
      If $data <> '' Then
          UDPShutdown()
          Local $unsignedHexValue = StringMid($data,83,8); Extract time from packet. Disregards the fractional second.
          Local $unsignedHexValue2 = StringMid($data,91,8); Extract time from packet. Disregards the fractional second.
          Local $value = UnsignedHexToDec($unsignedHexValue)
          Local $value2 = UnsignedHexToDec($unsignedHexValue2)
          Local $TZinfo = _Date_Time_GetTimeZoneInformation()
          Local $TZoffset = -(UnsignedToLong($TZinfo[1]) + (UnsignedToLong($TZinfo[4])*($TZinfo[0]<2)) + (UnsignedToLong($TZinfo[7])*($TZinfo[0]=2)))
          Local $UTC = _DateAdd('s',$value,'1900/01/01 00:00:00')
          Return _DateAdd('n',$TZoffset,$UTC)&"."&($value2)
      EndIf
      SetError(1)
   EndFunc

  Func MakePacket($d)
      Local $p = ''
      While $d
          $p &= Chr(Dec(StringLeft($d,2)))
          $d = StringTrimLeft($d,2)
      WEnd
      Return $p
   EndFunc

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


  Func UnsignedToLong($Value)
      Local Const $OFFSET_4 = 4294967296
      Local Const $MAXINT_4 = 2147483647
      If $Value < 0 Or $Value >= $OFFSET_4 Then Return SetError(1,0,$Value) ;' Overflow
      If $Value <= $MAXINT_4 Then
          Return $Value
      Else
          Return $Value - $OFFSET_4
      EndIf
   EndFunc

I couldnt figured out how to convert the fractions to a normal int or float

Introser
  • 161
  • 1
  • 12
  • 1
    Please provide more information about your uncomplete function/code. How the following functions look like: `MakePacket()`, `UnsignedHexToDec()`, `UnsignedToLong()`? Please also add your included scripts `#include ??.au3>` which you use. –  Feb 14 '19 at 19:54
  • Does your script refer to [post of user Rex at autoitscript](https://www.autoitscript.com/forum/topic/182902-autoit-real-time-server-with-inet)? –  Feb 14 '19 at 20:42
  • How did you print out the time with milliseconds like you wrote? –  Feb 14 '19 at 20:44
  • 1
    As in the following discussion described, there shouldn't be a way to get milliseconds: [StackOverflow](https://stackoverflow.com/questions/97853/whats-the-best-way-to-synchronize-times-to-millisecond-accuracy-and-precision-b). –  Feb 14 '19 at 21:04

1 Answers1

0

The fractional seconds are measured in units of 1/(2**numberOfBits) seconds = 0.000000000232831, where the numberOfBits is 4 Bytes * 8 bits/Byte = 32 bits. So the number of microseconds (6 decimal digits), for example, is: milliSecs = $value2 * 0.000232830643654. But displaying more than about 3 digits probably implies more accuracy than is true.

Andrew
  • 1
  • 4
  • 19