/* * Created on 22 juin 2005 * Created by Olivier Chalouhi * * Copyright (C) 2004, 2005, 2006 Aelitis SAS, All rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details ( see the LICENSE file ). * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * AELITIS, SAS au capital de 46,603.30 euros, * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. */ package com.aelitis.azureus.core.dht.netcoords.vivaldi.ver1.impl; import java.io.DataOutputStream; import java.io.IOException; import com.aelitis.azureus.core.dht.netcoords.DHTNetworkPosition; import com.aelitis.azureus.core.dht.netcoords.vivaldi.ver1.*; /** * * Vivaldi Papers : * http://www.sigcomm.org/sigcomm2004/papers/p426-dabek111111.pdf * */ public class VivaldiPositionImpl implements VivaldiPosition{ private static final float cc = 0.25f; private static final float ce = 0.5f; private static final float initial_error = 10f; private HeightCoordinatesImpl coordinates; private float error; private int nbUpdates; public VivaldiPositionImpl(HeightCoordinatesImpl coordinates) { this.coordinates = coordinates; error = initial_error; } public byte getPositionType() { return( DHTNetworkPosition.POSITION_TYPE_VIVALDI_V1 ); } public Coordinates getCoordinates() { return coordinates; } public double[] getLocation() { Coordinates coords = getCoordinates(); return( coords.getCoordinates()); } public float getErrorEstimate() { return error; } public void setErrorEstimate(float error) { this.error = error; } public void update(float rtt,Coordinates cj,float ej) { if ( valid(rtt) && valid(ej) && cj.isValid()){ // System.out.println( "accepted vivaldi update:" + rtt + "/" + cj + "/" + ej ); //Ensure we have valid data in input (clock changes lead to crazy rtt values) if(rtt <= 0 || rtt > 5*60*1000 ) return; if(error + ej == 0) return; //Sample weight balances local and remote error. (1) float w = error / (ej + error); //Real error float re = rtt - coordinates.distance(cj); //Compute relative error of this sample. (2) float es = Math.abs(re) / rtt; //Update weighted moving average of local error. (3) float new_error = es * ce * w + error * (1 - ce * w); //Update local coordinates. (4) float delta = cc * w; float scale = delta * re; HeightCoordinatesImpl random_error = new HeightCoordinatesImpl((float)Math.random()/10,(float)Math.random()/10,0f); HeightCoordinatesImpl new_coordinates = (HeightCoordinatesImpl)coordinates.add(coordinates.sub(cj.add(random_error)).unity().scale(scale)); if ( valid( new_error ) && new_coordinates.isValid()){ coordinates = new_coordinates; error = new_error > ERROR_MIN ? new_error : ERROR_MIN; }else{ /* not very interesting and occasionally happen... Debug.out( "VivaldiPosition: resetting as invalid: " + coordinates + "/" + error + " + " + rtt + "," + cj + "," + ej + "->" + new_coordinates + "/" + new_error ); */ coordinates = new HeightCoordinatesImpl(0,0,0); error = initial_error; } if(! cj.atOrigin()) { nbUpdates++; } if(nbUpdates > CONVERGE_EVERY) { nbUpdates = 0; update(10,new HeightCoordinatesImpl(0,0,0),CONVERGE_FACTOR); } }else{ // System.out.println( "rejected vivaldi update:" + rtt + "/" + cj + "/" + ej ); } } public boolean isValid() { return( (!Float.isNaN( getErrorEstimate())) && getCoordinates().isValid()); } private boolean valid( float f ) { return( !(Float.isInfinite( f ) || Float.isNaN( f ))); } public void update(float rtt, float[] data ){ update( rtt, new HeightCoordinatesImpl( data[0], data[1], data[2] ), data[3] ); } public float estimateRTT(Coordinates coordinates) { return this.coordinates.distance(coordinates); } public float[] toFloatArray(){ return( new float[]{ coordinates.getX(), coordinates.getY(), coordinates.getH(), error }); } public void fromFloatArray( float[] data ){ coordinates = new HeightCoordinatesImpl( data[0], data[1], data[2] ); error = data[3]; } public String toString() { return coordinates + " : " + error; } public boolean equals(Object arg0) { if(arg0 instanceof VivaldiPositionImpl) { VivaldiPositionImpl other = (VivaldiPositionImpl) arg0; if(other.error != error) return false; if(! other.coordinates.equals(coordinates)) return false; return true; } return false; } public float estimateRTT( DHTNetworkPosition _other ) { VivaldiPosition other = (VivaldiPosition)_other; Coordinates other_coords = other.getCoordinates(); if ( coordinates.atOrigin() || other_coords.atOrigin()){ return( Float.NaN ); } return( estimateRTT( other_coords )); } public void update( byte[] _other_id, DHTNetworkPosition _other, float rtt ) { VivaldiPositionImpl other = (VivaldiPositionImpl)_other; Coordinates other_coords = other.getCoordinates(); update( rtt, other_coords, other.getErrorEstimate()); } public int getSerialisedSize() { return( 16 ); // 4 floats } public void serialise( DataOutputStream os ) throws IOException { float[] data = toFloatArray(); for (int i=0;i<data.length;i++){ os.writeFloat( data[i] ); } } }