panda.net.ntp.TimeInfo Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of panda-nets Show documentation
Show all versions of panda-nets Show documentation
Panda Nets is a client side library of many basic Internet protocols.
package panda.net.ntp;
import java.net.DatagramPacket;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
/**
* Wrapper class to network time packet messages (NTP, etc) that computes related timing info and
* stats.
*/
public class TimeInfo {
private final NtpV3Packet _message;
private List _comments;
private Long _delay;
private Long _offset;
/**
* time at which time message packet was received by local machine
*/
private final long _returnTime;
/**
* flag indicating that the TimeInfo details was processed and delay/offset were computed
*/
private boolean _detailsComputed;
/**
* Create TimeInfo object with raw packet message and destination time received.
*
* @param message NTP message packet
* @param returnTime destination receive time
* @throws IllegalArgumentException if message is null
*/
public TimeInfo(NtpV3Packet message, long returnTime) {
this(message, returnTime, null, true);
}
/**
* Create TimeInfo object with raw packet message and destination time received.
*
* @param message NTP message packet
* @param returnTime destination receive time
* @param comments List of errors/warnings identified during processing
* @throws IllegalArgumentException if message is null
*/
public TimeInfo(NtpV3Packet message, long returnTime, List comments) {
this(message, returnTime, comments, true);
}
/**
* Create TimeInfo object with raw packet message and destination time received. Auto-computes
* details if computeDetails flag set otherwise this is delayed until computeDetails() is
* called. Delayed computation is for fast intialization when sub-millisecond timing is needed.
*
* @param msgPacket NTP message packet
* @param returnTime destination receive time
* @param doComputeDetails flag to pre-compute delay/offset values
* @throws IllegalArgumentException if message is null
*/
public TimeInfo(NtpV3Packet msgPacket, long returnTime, boolean doComputeDetails) {
this(msgPacket, returnTime, null, doComputeDetails);
}
/**
* Create TimeInfo object with raw packet message and destination time received. Auto-computes
* details if computeDetails flag set otherwise this is delayed until computeDetails() is
* called. Delayed computation is for fast intialization when sub-millisecond timing is needed.
*
* @param message NTP message packet
* @param returnTime destination receive time
* @param comments list of comments used to store errors/warnings with message
* @param doComputeDetails flag to pre-compute delay/offset values
* @throws IllegalArgumentException if message is null
*/
public TimeInfo(NtpV3Packet message, long returnTime, List comments, boolean doComputeDetails) {
if (message == null) {
throw new IllegalArgumentException("message cannot be null");
}
this._returnTime = returnTime;
this._message = message;
this._comments = comments;
if (doComputeDetails) {
computeDetails();
}
}
/**
* Add comment (error/warning) to list of comments associated with processing of NTP parameters.
* If comment list not create then one will be created.
*
* @param comment the comment
*/
public void addComment(String comment) {
if (_comments == null) {
_comments = new ArrayList();
}
_comments.add(comment);
}
/**
* Compute and validate details of the NTP message packet. Computed fields include the offset
* and delay.
*/
public void computeDetails() {
if (_detailsComputed) {
return; // details already computed - do nothing
}
_detailsComputed = true;
if (_comments == null) {
_comments = new ArrayList();
}
TimeStamp origNtpTime = _message.getOriginateTimeStamp();
long origTime = origNtpTime.getTime();
// Receive Time is time request received by server (t2)
TimeStamp rcvNtpTime = _message.getReceiveTimeStamp();
long rcvTime = rcvNtpTime.getTime();
// Transmit time is time reply sent by server (t3)
TimeStamp xmitNtpTime = _message.getTransmitTimeStamp();
long xmitTime = xmitNtpTime.getTime();
/*
* Round-trip network delay and local clock offset (or time drift) is calculated according
* to this standard NTP equation: LocalClockOffset = ((ReceiveTimestamp -
* OriginateTimestamp) + (TransmitTimestamp - DestinationTimestamp)) / 2 equations from
* RFC-1305 (NTPv3) roundtrip delay = (t4 - t1) - (t3 - t2) local clock offset = ((t2 - t1)
* + (t3 - t4)) / 2 It takes into account network delays and assumes that they are
* symmetrical. Note the typo in SNTP RFCs 1769/2030 which state that the delay is (T4 - T1)
* - (T2 - T3) with the "T2" and "T3" switched.
*/
if (origNtpTime.ntpValue() == 0) {
// without originate time cannot determine when packet went out
// might be via a broadcast NTP packet...
if (xmitNtpTime.ntpValue() != 0) {
_offset = Long.valueOf(xmitTime - _returnTime);
_comments.add("Error: zero orig time -- cannot compute delay");
}
else {
_comments.add("Error: zero orig time -- cannot compute delay/offset");
}
}
else if (rcvNtpTime.ntpValue() == 0 || xmitNtpTime.ntpValue() == 0) {
_comments.add("Warning: zero rcvNtpTime or xmitNtpTime");
// assert destTime >= origTime since network delay cannot be negative
if (origTime > _returnTime) {
_comments.add("Error: OrigTime > DestRcvTime");
}
else {
// without receive or xmit time cannot figure out processing time
// so delay is simply the network travel time
_delay = Long.valueOf(_returnTime - origTime);
}
// TODO: is offset still valid if rcvNtpTime=0 || xmitNtpTime=0 ???
// Could always hash origNtpTime (sendTime) but if host doesn't set it
// then it's an malformed ntp host anyway and we don't care?
// If server is in broadcast mode then we never send out a query in first place...
if (rcvNtpTime.ntpValue() != 0) {
// xmitTime is 0 just use rcv time
_offset = Long.valueOf(rcvTime - origTime);
}
else if (xmitNtpTime.ntpValue() != 0) {
// rcvTime is 0 just use xmitTime time
_offset = Long.valueOf(xmitTime - _returnTime);
}
}
else {
long delayValue = _returnTime - origTime;
// assert xmitTime >= rcvTime: difference typically < 1ms
if (xmitTime < rcvTime) {
// server cannot send out a packet before receiving it...
_comments.add("Error: xmitTime < rcvTime"); // time-travel not allowed
}
else {
// subtract processing time from round-trip network delay
long delta = xmitTime - rcvTime;
// in normal cases the processing delta is less than
// the total roundtrip network travel time.
if (delta <= delayValue) {
delayValue -= delta; // delay = (t4 - t1) - (t3 - t2)
}
else {
// if delta - delayValue == 1 ms then it's a round-off error
// e.g. delay=3ms, processing=4ms
if (delta - delayValue == 1) {
// delayValue == 0 -> local clock saw no tick change but destination clock
// did
if (delayValue != 0) {
_comments.add("Info: processing time > total network time by 1 ms -> assume zero delay");
delayValue = 0;
}
}
else {
_comments.add("Warning: processing time > total network time");
}
}
}
_delay = Long.valueOf(delayValue);
if (origTime > _returnTime) {
_comments.add("Error: OrigTime > DestRcvTime");
}
_offset = Long.valueOf(((rcvTime - origTime) + (xmitTime - _returnTime)) / 2);
}
}
/**
* Return list of comments (if any) during processing of NTP packet.
*
* @return List or null if not yet computed
*/
public List getComments() {
return _comments;
}
/**
* Get round-trip network delay. If null then could not compute the delay.
*
* @return Long or null if delay not available.
*/
public Long getDelay() {
return _delay;
}
/**
* Get clock offset needed to adjust local clock to match remote clock. If null then could not
* compute the offset.
*
* @return Long or null if offset not available.
*/
public Long getOffset() {
return _offset;
}
/**
* Returns NTP message packet.
*
* @return NTP message packet.
*/
public NtpV3Packet getMessage() {
return _message;
}
/**
* Get host address from message datagram if available
*
* @return host address of available otherwise null
*/
public InetAddress getAddress() {
DatagramPacket pkt = _message.getDatagramPacket();
return pkt == null ? null : pkt.getAddress();
}
/**
* Returns time at which time message packet was received by local machine.
*
* @return packet return time.
*/
public long getReturnTime() {
return _returnTime;
}
/**
* Compares this object against the specified object. The result is true
if and
* only if the argument is not null
and is a TimeStamp
object that
* contains the same values as this object.
*
* @param obj the object to compare with.
* @return true
if the objects are the same; false
otherwise.
*/
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null || getClass() != obj.getClass()) {
return false;
}
TimeInfo other = (TimeInfo)obj;
return _returnTime == other._returnTime && _message.equals(other._message);
}
/**
* Computes a hashcode for this object. The result is the exclusive OR of the return time and
* the message hash code.
*
* @return a hash code value for this object.
*/
@Override
public int hashCode() {
final int prime = 31;
int result = (int)_returnTime;
result = prime * result + _message.hashCode();
return result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy