fundamental / rtosc

Realtime Safe OSC packet serialization and dispatch
https://fundamental-code.com/wiki/rtosc/
MIT License
85 stars 16 forks source link

Documentation for timestamp format #29

Closed xavriley closed 8 years ago

xavriley commented 8 years ago

Regarding emplace_uint64 I'm having a hard time formatting the input timestamp in such a way as to produce valid output. As I've read it so far, I need to add the magic number to make the Unix timestamp count from 1900, and then pack the seconds and fractions in a double manually before handing it to rtosc_bundle - is that correct? From that point, is emplace then reversing the endianness?

If so it might be useful to provide some kind of helper function that takes a Unix timestamp as a float or something. If that's not desirable, any advice on formatting would be very welcome as I'm struggling to get this working with my Ruby wrapper at the moment.

fundamental commented 8 years ago

From the OSC 1.0 spec http://opensoundcontrol.org/node/3/#timetags

Time tags are represented by a 64 bit fixed point number. The first 32 bits specify the number of seconds since midnight on January 1, 1900, and the last 32 bits specify fractional parts of a second to a precision of about 200 picoseconds. This is the representation used by Internet NTP timestamps.The time tag value consisting of 63 zero bits followed by a one in the least signifigant bit is a special case meaning "immediately."

I think the most idiomatic ruby way of writing that is

(Time.new-Time.new("1900-01-01"))*(2**32)

which would end up losing some precision, but it should still be useful enough. The resulting floating point number would just be converted to a 64 bit integer and that should be passed in as the time tag.

samaaron commented 8 years ago

Wouldn't it make more sense to do this on the C side?

xavriley commented 8 years ago

My apologies. I was getting to the end of my tether before opening this as I thought there was something special going on with emplace_uint64 that was changing my input, but it turns out I wasn't dereferencing a pointer in the approach I was trying.

I've ended up with a much simpler method of merging the uint32s for second and fraction into a uint64_t which I've gone with here:

uint64_t ruby_time_to_osc_timetag(VALUE rubytime) {
  uint64_t timetag;
  double floattime;
  uint32_t sec;
  uint32_t frac;

  switch(TYPE(rubytime)) {
    case T_NIL:
      timetag = 1;
      break;
    default:
      // convert Time object to ntp
      floattime = JAN_1970 + NUM2DBL(rb_funcall(rubytime, rb_intern("to_f"), 0));

      sec = NUM2UINT(DBL2NUM(floattime));
      frac = (int)(fmod(floattime, 1.0) * 4294967296); // * (2 ** 32)
      timetag = (uint64_t)((uint64_t)sec << 32 | (uint64_t)frac);
      break;
  }

  return timetag;
}

https://github.com/xavriley/fast_osc/blob/master/ext/fast_osc/fast_osc_wrapper.c#L187

My tests are all passing now so I'll just leave that in case anyone else finds it useful. Thanks again for the help.

fundamental commented 8 years ago

@samaaron A simple routine to convert unix time to timetags (i.e. seconds + nanoseconds) and vice versa might be an appropriate addition to the core API. I don't use timetags much in my own OSC use, but I'd be fine with a patch along those lines.