10 // GenerateClients returns a slice with nodes number of clients, all with the
12 func GenerateClients(nodes int, config *Config) ([]*Client, error) {
13 clients := make([]*Client, nodes)
14 for i, _ := range clients {
15 client, err := NewClient(config)
25 // GenerateLine returns a truth matrix as if all the nodes are in a straight linke
26 // with the given spacing between them.
27 func GenerateLine(nodes int, spacing time.Duration) [][]time.Duration {
28 truth := make([][]time.Duration, nodes)
29 for i := range truth {
30 truth[i] = make([]time.Duration, nodes)
33 for i := 0; i < nodes; i++ {
34 for j := i + 1; j < nodes; j++ {
35 rtt := time.Duration(j-i) * spacing
36 truth[i][j], truth[j][i] = rtt, rtt
42 // GenerateGrid returns a truth matrix as if all the nodes are in a two dimensional
43 // grid with the given spacing between them.
44 func GenerateGrid(nodes int, spacing time.Duration) [][]time.Duration {
45 truth := make([][]time.Duration, nodes)
46 for i := range truth {
47 truth[i] = make([]time.Duration, nodes)
50 n := int(math.Sqrt(float64(nodes)))
51 for i := 0; i < nodes; i++ {
52 for j := i + 1; j < nodes; j++ {
53 x1, y1 := float64(i%n), float64(i/n)
54 x2, y2 := float64(j%n), float64(j/n)
55 dx, dy := x2-x1, y2-y1
56 dist := math.Sqrt(dx*dx + dy*dy)
57 rtt := time.Duration(dist * float64(spacing))
58 truth[i][j], truth[j][i] = rtt, rtt
64 // GenerateSplit returns a truth matrix as if half the nodes are close together in
65 // one location and half the nodes are close together in another. The lan factor
66 // is used to separate the nodes locally and the wan factor represents the split
67 // between the two sides.
68 func GenerateSplit(nodes int, lan time.Duration, wan time.Duration) [][]time.Duration {
69 truth := make([][]time.Duration, nodes)
70 for i := range truth {
71 truth[i] = make([]time.Duration, nodes)
75 for i := 0; i < nodes; i++ {
76 for j := i + 1; j < nodes; j++ {
78 if (i <= split && j > split) || (i > split && j <= split) {
81 truth[i][j], truth[j][i] = rtt, rtt
87 // GenerateCircle returns a truth matrix for a set of nodes, evenly distributed
88 // around a circle with the given radius. The first node is at the "center" of the
89 // circle because it's equidistant from all the other nodes, but we place it at
90 // double the radius, so it should show up above all the other nodes in height.
91 func GenerateCircle(nodes int, radius time.Duration) [][]time.Duration {
92 truth := make([][]time.Duration, nodes)
93 for i := range truth {
94 truth[i] = make([]time.Duration, nodes)
97 for i := 0; i < nodes; i++ {
98 for j := i + 1; j < nodes; j++ {
103 t1 := 2.0 * math.Pi * float64(i) / float64(nodes)
104 x1, y1 := math.Cos(t1), math.Sin(t1)
105 t2 := 2.0 * math.Pi * float64(j) / float64(nodes)
106 x2, y2 := math.Cos(t2), math.Sin(t2)
107 dx, dy := x2-x1, y2-y1
108 dist := math.Sqrt(dx*dx + dy*dy)
109 rtt = time.Duration(dist * float64(radius))
111 truth[i][j], truth[j][i] = rtt, rtt
117 // GenerateRandom returns a truth matrix for a set of nodes with normally
118 // distributed delays, with the given mean and deviation. The RNG is re-seeded
119 // so you always get the same matrix for a given size.
120 func GenerateRandom(nodes int, mean time.Duration, deviation time.Duration) [][]time.Duration {
123 truth := make([][]time.Duration, nodes)
124 for i := range truth {
125 truth[i] = make([]time.Duration, nodes)
128 for i := 0; i < nodes; i++ {
129 for j := i + 1; j < nodes; j++ {
130 rttSeconds := rand.NormFloat64()*deviation.Seconds() + mean.Seconds()
131 rtt := time.Duration(rttSeconds * secondsToNanoseconds)
132 truth[i][j], truth[j][i] = rtt, rtt
138 // Simulate runs the given number of cycles using the given list of clients and
139 // truth matrix. On each cycle, each client will pick a random node and observe
140 // the truth RTT, updating its coordinate estimate. The RNG is re-seeded for
141 // each simulation run to get deterministic results (for this algorithm and the
142 // underlying algorithm which will use random numbers for position vectors when
143 // starting out with everything at the origin).
144 func Simulate(clients []*Client, truth [][]time.Duration, cycles int) {
147 nodes := len(clients)
148 for cycle := 0; cycle < cycles; cycle++ {
149 for i, _ := range clients {
150 if j := rand.Intn(nodes); j != i {
151 c := clients[j].GetCoordinate()
153 node := fmt.Sprintf("node_%d", j)
154 clients[i].Update(node, c, rtt)
160 // Stats is returned from the Evaluate function with a summary of the algorithm
167 // Evaluate uses the coordinates of the given clients to calculate estimated
168 // distances and compares them with the given truth matrix, returning summary
170 func Evaluate(clients []*Client, truth [][]time.Duration) (stats Stats) {
171 nodes := len(clients)
173 for i := 0; i < nodes; i++ {
174 for j := i + 1; j < nodes; j++ {
175 est := clients[i].DistanceTo(clients[j].GetCoordinate()).Seconds()
176 actual := truth[i][j].Seconds()
177 error := math.Abs(est-actual) / actual
178 stats.ErrorMax = math.Max(stats.ErrorMax, error)
179 stats.ErrorAvg += error
184 stats.ErrorAvg /= float64(count)
185 fmt.Printf("Error avg=%9.6f max=%9.6f\n", stats.ErrorAvg, stats.ErrorMax)