Skip to content

Instantly share code, notes, and snippets.

@kmmbvnr
Created April 25, 2013 03:21
Show Gist options
  • Save kmmbvnr/5457279 to your computer and use it in GitHub Desktop.
Save kmmbvnr/5457279 to your computer and use it in GitHub Desktop.
Simple Time Sync algorithm impl
package com.kmmbvnr;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Simple Time Sync algorithm impl
* Based on http://www.mine-control.com/zack/timesync/timesync.html
*/
public class TimeSyncService {
private long _timeshift;
private boolean _firstResponse = true;
private List<Roundtrip> _roundTrips = new LinkedList<Roundtrip>();
class Roundtrip implements Comparable<Roundtrip> {
long _sent_time;
long _server_time;
long _receive_time;
public Roundtrip(long sent_time, long server_time, long receive_time) {
_sent_time = sent_time;
_server_time = server_time;
_receive_time = receive_time;
}
public long latency() {
return _receive_time - _sent_time;
}
public long timeshift() {
return (_server_time + this.latency()/2)-_receive_time;
}
@Override
public int compareTo(Roundtrip that) {
return Long.compare(this.latency(), that.latency());
}
@Override
public String toString() {
return String.format("(%s, %s, %s)", _sent_time, _server_time, _receive_time);
}
}
public TimeSyncService() {
_timeshift = 0;
}
public long getCurrentTimestamp() {
return System.currentTimeMillis()/1000L + _timeshift;
}
public void collectResponse(long sent_timestamp, long server_timestamp) {
Roundtrip roundtrip = new Roundtrip(sent_timestamp, server_timestamp, this.getCurrentTimestamp());
if(_firstResponse) {
_firstResponse = false;
_timeshift = roundtrip.timeshift();
} else {
_roundTrips.add(roundtrip);
}
}
public void finish() {
Collections.sort(_roundTrips);
double latency_mean = 0;
for(Roundtrip roundTrip : _roundTrips) {
latency_mean += (1.0 * roundTrip.latency()) / _roundTrips.size();
}
double std_dev = 0;
for(Roundtrip roundTrip : _roundTrips) {
std_dev += Math.pow(roundTrip.latency() - latency_mean, 2)/ _roundTrips.size();
}
std_dev = Math.sqrt(std_dev);
double latency_median = _roundTrips.get(_roundTrips.size()/2).latency();
int count = 0;
long mean_timeshift = 0;
for(Roundtrip roundTrip : _roundTrips) {
if(latency_median - std_dev < roundTrip.latency() && roundTrip.latency() < latency_median + std_dev) {
mean_timeshift += roundTrip.timeshift();
count += 1;
}
}
_timeshift = mean_timeshift/count;
}
@org.junit.Test
public void testRoundtrip() {
Roundtrip roundtrip = new Roundtrip(1, 6, 3);
org.junit.Assert.assertEquals(2, roundtrip.latency());
org.junit.Assert.assertEquals(4, roundtrip.timeshift());
}
@org.junit.Test
public void testTimesync() {
_roundTrips.add(new Roundtrip(1, 6, 3));
_roundTrips.add(new Roundtrip(3, 8, 5));
_roundTrips.add(new Roundtrip(5, 10, 7));
_roundTrips.add(new Roundtrip(7, 200, 1000));
_roundTrips.add(new Roundtrip(1000, 1005, 1002));
this.finish();
org.junit.Assert.assertEquals(4, this._timeshift);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment