dis-tutorial

##Timestamps

Classically DIS uses UDP messages to transmit state information. But there’s a problem here: UDP datagrams are unreliable by design. Once sent, they may not arrive. They can be dropped by the network or the receiving host if traffic is too high, or for any other reason. The network makes a best-effort to deliver them, but delivery is not guaranteed. Datagrams may arrive out of order; a message sent before another may arrive later. And, rarely, some packets may be duplicated, such that two copies of the same message arrive. These outcomes are all the results of tradeoffs; TCP sockets avoid these issues in exchange for creating others.

Virtual world designers have developed ways to work around the limitations of UDP. In the case of DIS, a timestamp is added to the payload of each PDU sent.

In the PDU header section that is a part of every PDU there as a field called, oddly enough, the timestamp field. This contains a representation of the time at which the data in the PDU was generated. “The time at which the data was generated” is itself a slippery concept. If there are two hosts on the network, each with their own clock, do the two clocks have the same time? Perhaps, or close enough, if both hosts are using Network Time Protocol (NTP). NTP can synchronize computer clocks to within a few milliseconds in most instances. If a host isn’t using NTP it’s very easy for the clocks to diverge by over a minute.

The algorithm DIS uses to detect out of order or duplicate PDUs simple enough. Imagine an entity state PDU arriving with a state information update. It has a timestamp. We compare the timestamp to the timestamp of the last ESPDU we received for this entity. If the timestamp is less than the last update, we’ve received an older update that has arrived out of order. The usual procedure is to simply discard the newly arrived PDU. We already have more recent state information, and have no need to update the simulation with old data.

If the timestamp of the PDU is equal to the timestamp of the last ESDPU received, we probably have a duplicate packet, and it can likewise be discarded. In reality what probably happened is that the programmer on the sending side neglected to update the timestamp field. This causes all the incoming packets to be discarded in the false belief that we are receiving duplicates.

The format of the DIS timestamp is needlessly complex. It has two modes: absolute timestamp and relative timestamp. The field is an unsigned 32 bit integer. The least-significant bit is a flag that determines the mode. If the LSB is set to one, it is in “absolute time” mode, and if it’s 0, it is in “relative time mode”. Absolute time mode means that the sending host is using NTP or otherwise as a clock that is synchronized with other hosts. Relative time mode means that the sending host is relying on its own clock, and that may have only a passing relationship to the actual time. As far as the receiving application is concerned, the two modes don’t matter a lot. If we’re only trying to detect duplicate or out of order packets, either mode works the same.

The units used in the timestamp are a synthetic time unit that record time since the top of the hour. 2^31 bits are available in the field after subtracting one to hold the timestamp mode. The hour is divided into 2^31 units, resulting in a time unit that is about 1.68 μs long. This is rather optimistic precision.

In the wild some DIS implementations simply ignore the standard and use some variant of Unix time since the epoch. (Hopefully with the LSB set to zero, signifying that this is not an absolute timestamp.) This doesn’t work all that well; if you use units of seconds the precision isn’t very good, and if you use units of milliseconds the 32-bit field isn’t large enough to hold current time values. You can use milliseconds since the start of the week or some such scheme. Or just increment the timestamp field by one for every PDU sent. I’d avoid these techniques. If you’re archiving PDUs for later analysis and playback this can cause havoc with the playback because it’s essential to play back PDUs using the same time intervals as which they were sent. If you’re using a commercial archive analysis and playback tool it’s unlikely the tool will recognize whatever it is that you did.

Another problem is rollover of the timestamp field. If time units since the top of the hour is used, what happens when the next hour arrives? The timestamp will flip back to zero and start counting up again. But of course this upsets the naive algorithm described above, which assumed that the timestamp was monotonically increasing. A common way to handle this is to determine if the new incoming timestamp is far less than the last received timestamp. If the incoming timestamp is more than 50% less than the range of the counter, the odds are good that we have seen a rollover and that we have passed the hour boundary.