views:

457

answers:

2

I need to calculate an ntp time stamp using gettimeofday. Below is how I've done it with comments on method. Look good to you guys? (minus error checking). Also, here's a codepad link.

#include <unistd.h>
#include <sys/time.h>

const unsigned long EPOCH = 2208988800UL; // delta between epoch time and ntp time
const double NTP_SCALE_FRAC = 4294967295.0; // maximum value of the ntp fractional part

int main()
{
  struct timeval tv;
  uint64_t ntp_time;
  uint64_t tv_ntp;
  double tv_usecs;

  gettimeofday(&tv, NULL);
  tv_ntp = tv.tv_sec + EPOCH;

  // convert tv_usec to a fraction of a second
  // next, we multiply this fraction times the NTP_SCALE_FRAC, which represents
  // the maximum value of the fraction until it rolls over to one. Thus,
  // .05 seconds is represented in NTP as (.05 * NTP_SCALE_FRAC)
  tv_usecs = (tv.tv_usec * 1e-6) * NTP_SCALE_FRAC;

  // next we take the tv_ntp seconds value and shift it 32 bits to the left. This puts the 
  // seconds in the proper location for NTP time stamps. I recognize this method has an 
  // overflow hazard if used after around the year 2106
  // Next we do a bitwise OR with the tv_usecs cast as a uin32_t, dropping the fractional
  // part
  ntp_time = ((tv_ntp << 32) | (uint32_t)tv_usecs);
}
A: 

There is no need to use uint64_t here - unsigned long long is guaranteed to be at least 64 bits wide.

You also don't need to go to and from double, since NTP_SCALE_FRAC * 1000000 will easily fit in an unsigned long long.

EPOCH should be unsigned long long, not unsigned long, so that the addition with tv.tv_sec doesn't wrap around.

All up:

const unsigned long long EPOCH = 2208988800ULL;
const unsigned long long NTP_SCALE_FRAC = 4294967295ULL;

unsigned long long tv_to_ntp(struct timeval tv)
{
    unsigned long long tv_ntp, tv_usecs;

    tv_ntp = tv.tv_sec + EPOCH;
    tv_usecs = (NTP_SCALE_FRAC * tv.tv_usec) / 1000000UL;

    return (tv_ntp << 32) | tv_usecs;
}
caf
I might be misunderstanding, but the spec for long long is that it's at least 64, but could be more. I have to do some byte swapping on the results (network byte order), so I wanted to more tightly constrain the size.
jkyle
Yes, it could be more. If you need to manipulate the underlying representation (say, to stuff it into a network packet), then using `uint64_t` is appropriate, but you should generally limit that use to the network-handling packing/unpacking code itself.
caf
A: 

can i ask why you multiply tv_usec with NTP_SCALE_FRAC? to me, it seems trasnforming usec to pico seconds (*10^6) should be sufficient, maybe i am missing something?

agglos
In NTP, if I understand correctly, the fraction portion is represented as a fraction of the total possible values. So it's not a 1-to-1.For example, if I represent one second in 1000 parts, then someone gives me a value of .5 seconds with base 60, I'd have to scale that to my on base 1000. With NTP, the base is the 32 bit representation it's transformed into. So 1 second = 2^32 ntp fraction seconds.
jkyle
Thanks, I understand this. What is not apparent to me is whytv_usecs = (NTP_SCALE_FRAC * tv.tv_usec) / 1000000ULdoes the job?
agglos