Created
April 25, 2013 03:21
-
-
Save kmmbvnr/5457279 to your computer and use it in GitHub Desktop.
Simple Time Sync algorithm impl
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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